Difference between different refspecs in a git-pull call

后端 未结 3 1073
后悔当初
后悔当初 2021-01-17 03:34

Whats the difference between using the following git commands

git pull origin master
git pull origin master:master

Some ob

相关标签:
3条回答
  • 2021-01-17 04:04

    This is a bit tricky, so let's deal with it one bit at a time.

    git pull rolls like this:

    1. Fetches the given refs1 (the second argument in your examples, which is called the refspec — a portmaneu of "reference specification") from the given remote (the first argument in your example).

      If the remote argument is missing, Git tries to obtain it using the branch.<name>.remote configuration variable in the local repository, where <name> is the name of the currently checked out branch.

      If the refspec argument is missing, Git tries to obtain it using the branch.<name>.merge configuration variable in the local repository, where <name> means the same thing as above.

    2. Merges all the fetched refs to the currently checked out branch, so @Oznerol256 is incorrect.

    Now let's explain what's the difference between the refspecs master and master:master when it comes to git pull

    git pull passes the refspec directly to git fetch, and it parses the refspec in the following way: "take from the remote all the refs matching the spec on the left side of : and possibly use them to update matching refs in the local repository, which specified by the spec on the right side of :". The crucial bit here is that if there's no : in the refspec, or there's nothing to the right of it, this is interpreted as "update nothing" by git fetch.

    Now let's dig deeper. According to the rules of interpretation of refspecs, bare "master" is (in most cases2) interpreted as refs/heads/master, which means "the branch named «master»".

    Okay, now it should be clear that git pull origin master:

    1. Calls git fetch origin master whch fetches refs/heads/master from the remote indicated by origin and just merely stores the fetched objects in the database (plus updates the special ref FETCH_HEAD). It does not update any branches or tags in your local repository.

    2. Calls git merge FETCH_HEAD which attempts to merge the state of refs/heads/master as fetched from the remote repository into the currently checked out branch.

      Obviously, this might result in conflicts, and that's what you're observing in the first case.

    Now let's dig yet more deeper. As should be clear by now, the master:master refspec (usually2) expands to refs/heads/master:refs/heads/master, and so git pull origin master:master rolls like this:

    1. It calls git fetch origin master:master which

      1. Fetches refs/heads/master from the remote and
      2. Updates local refs/heads/master by the fetched objects.

        This might fail with the "non-fast forward" error, if the local "master" is not wholly contained in the remote's "master", and that's what you're observing.

    At this point no merging is attempted since the first step generated an error.

    It should be noted that neither of your examples properly updates local refs: the first one just does not attempt this, and the second one tries to update a supposedly wrong ref — the correct call would be git pull origin +refs/heads/master:refs/remotes/origin/masterwhich would forcibly (hence the +) update the proper remote branch and then attempt to merge what was fetched into the currently checked out branch.

    To understand why such a "strange" refspec is used, let's see what refspec Git uses when you call git fetch origin — since in this case it reads the remote.<remotename>.fetch configuration variable in the local repository (this variable is created by git remote add or git clone):

    $ git config --local --get remote.origin.fetch
    +refs/heads/*:refs/remotes/origin/*
    

    As you can see, it tells git fetch to force updates and to update remote branches.

    It could be seen by now that git pull is frequently and mindlessly overused without actually understanding its inner workings. In my opinion, it's way better to use two step operation instead of pulling:

    1. git fetch origin — to update remote branches.
    2. git merge origin/master — to merge the state of "master" as last seen on "origin" into the currently checked out branch.

      If the currently checked out branch is set to track a remote branch which you want to merge, the Git call becomes even simpler:

      git merge @{u}
      

    I would also recommend reading this article.


    1 A "ref" in Git parlance is a named entity which points to a commit (simple or direct ref) or to another ref (a symbolic ref — HEAD is a symbolic ref ). Branches and tags are examples of simple refs, HEAD might be both: when you have a branch checked out it's a symbolic ref, when you have anything else checked out (and hence are in the "detached HEAD" state) it's a simple ref.

    2 If there's a tag and a branch named "master", the refspec will be resolved as the name of the tag — tags have precedence. In a situation like this, a full ref name could be used to designate the branch.

    0 讨论(0)
  • 2021-01-17 04:10

    The first one tells git to pull the branch master from the remote origin. It doesn't tell git where to merge the fetched commits into. It uses the specified merge key in the configuration.

    The second one tells git to pull the branch master from the remove origin and merge it into the local branch master. This overrides the merge key in the configuration.

    0 讨论(0)
  • 2021-01-17 04:17

    A git pull inherently performs two operations: First, a git fetch, followed by git merge.

    With git pull origin master, the master branch of your origin remote will be fetched (retrieved), then merged into your current, checked-out branch.

    By defining two branch names, you are specifying a refspec of which branch is merged into which.

    The generalized example reads as follows: "Retrieve the source branch from the specified remote, merge it with the destination branch.

    git pull <remote> <source>:<destination>

    0 讨论(0)
提交回复
热议问题