git pull command output message meaning into which branch

扶醉桌前 提交于 2019-12-01 00:44:15
torek

Warning: long. TL;DR version: you are looking at git fetch output and git fetch is not affecting your master at all, it's the git merge part of your git pull that affects your master. However, your git fetch is updating a remote-tracking branch origin/br1 and in one case updating or even creating a local branch br1.

git pull is a convenience script

Always remember that git pull is just a convenience script that runs two other git commands for you: first, git pull passes your arguments on to git fetch. Once that finishes, git pull runs git merge (or, if instructed, git rebase), but all of the quoted action in your original question is occurring purely in git fetch. (A bit in the "update" section is from git merge, which I'll get to later.)

If you do not provide a remote argument to git pull, the pull script extracts one from your current branch's configuration. In this case, the one it extracts is clearly origin. So If you run git pull without specifying origin, you are, in effect, running git pull origin.

If you do not provide a refspec argument to git pull, the pull script extracts a very simple one from your current branch's configuration—in this case, whatever you see from git config --get branch.master.merge, which apparently is br1. So this means that if you run git pull origin, you are in effect running git pull origin br1.1

Again, all of this is then just passed on to git fetch, so whether you run git pull, git pull origin, or git pull origin br1, all of those just wind up invoking:

git fetch origin br1

(which you can also do manually, and you'll see the above).

We'll get to git fetch origin br1:br1 later, below.

Background on possible misconceptions

Let's take a brief look at your setup statement again:

Say there is a remote branch br1 checkout on the remote repo, and the master branch on a local repo.

What branch, if any, is currently checked-out on the remote is mostly irrelevant for fetch. The first (or first-enough) thing fetch does is to connect to the remote and ask it for a list of all references and their corresponding SHA-1s (you can see what git fetch can see by running git ls-remote). The remote's HEAD is included in that list, and this allows you to direct your fetch to use it, but if you don't, your fetch just ignores it (the remote's HEAD is mostly only used for controlling the default initial branch on an initial git clone).

The current branch in your local repo is important, though, for two reasons:

  • if you don't supply extra arguments to git pull, it finds them based on your current branch; and
  • after the fetch succeeds, git pull runs either git merge or git rebase, which uses your current branch.

Again, your current branch is master so pull will use branch.master.remote and branch.master.merge as the default remote and refspec arguments.2 This is how we can deduce, from the original output, that these are origin and br1 respectively.

On git fetch

Returning to git fetch, what it does is to confer with the remote git server a bit to find out what references (branches and tags, mostly) are available and what their corresponding SHA-1 values are. Once it has that information, it then looks at which references you've asked it to bring over. If you listed a specific reference like br1, that's the one reference it will bring over.

Along with each reference, of course, it has to bring any new objects (the commit itself, and its associated trees and files, plus any parent commits and their trees-and-files as needed) so that you get all the history from that particular point backwards. Whatever history you already have, of course, it can skip.3

As VonC already noted, git's behavior on git fetch remote refspec has changed in git 1.8.4 and later. It used to be the case that if you ran git fetch remote refspec, your refspec overrode the rules in your git configuration entry for that remote, but now it simply selects from them. By default, the rule-set for the remote named origin is +refs/heads/*:refs/remotes/origin/*, so your br1 refspec selects an item from this rule-set.


Let's pause to see what happens if you run git fetch with only three arguments, like this:

$ git fetch origin

Here you are instructing your local git to connect to the remote, find out what it has, and bring over all branches. The way (and reason) it does that is just as outlined above: it connects, gets a list, and then consults the output of git config --get-all remote.origin.fetch.4 This is a list of "refspecs", one per git config --get-all line.

Since the standard line (one single line) for remote.origin.fetch is +refs/heads/*:refs/remotes/origin/*, your local git will take every reference-name that matches refs/heads/*. That is, it will take all branches on origin, because branches are simply "references whose names start with refs/heads/". What it does with those branches is determined by the right-hand side of this refspec: it replaces refs/heads/ with refs/remotes/origin/.

The result is a "remote-tracking branch". If the remote has a branch master, your local git translates this to origin/master. If the remote has a br1, your local git translates that to origin/br1. For each branch on the remote, you get a (local) remote-tracking branch whose name starts with origin/.5


Returning to our case of git fetch origin br1, we can now see what happens: our local git brings over br1, which turns out to be a branch so that its full name is refs/heads/br1. Because of that, it matches the standard remote.origin.fetch line and refs/heads/br1 is translated to refs/remotes/origin/br1, which causes git to print out origin/br1:

9188a5d..97d4825  br1        -> origin/br1

The name br1 on the left is the short name of the reference on the remote, and the name origin/br1 on the right is the short name of the reference that git fetch has updated.

In the past you would instead see something like this—and you still may see it:

* branch            name       -> FETCH_HEAD

This indicates that git fetch found a branch named name (i.e., a reference of the form refs/heads/name) on the remote and brought it over to your local repo and put it in FETCH_HEAD. What is FETCH_HEAD? It's a special file that exists pretty much just for the git pull script. (It works a lot like a reference, but it has a special format and may contains multiple SHA-1s.)


Now we're (finally) ready to tackle the br1:br1 case. Here, you're telling your local git fetch to bring over reference br1. It does the usual—call up the remote, discover that br1 is really refs/heads/br1, and bring over the reference and any needed objects—but this time, in addition to consulting the remote.origin.fetch line, it writes the new SHA-1 into the reference you specified.

In this case, you specified br1 with no qualifications: not refs/heads/br1, not refs/remotes/origin/br1, just br1. In this case, git sees that it's a refs/heads/ reference on the remote, meaning it's a branch; so git adds refs/heads/ on your end as well, and creates or updates your own refs/heads/br1.

In other words, this creates or updates your local branch br1.

In addition, git still applies the remote.origin.fetch line, which is still +refs/heads/*:refs/remotes/origin/*, so it still updates your remote-tracking branch origin/br1 (full name refs/remotes/origin/br1). That's why you got the output you got from Command 1.

On git merge

What about FETCH_HEAD? Well, that's where the rest of git pull comes back in: after doing the git fetch step, the pull script runs either git merge or git rebase. What it merges (or rebases-on-to) is whatever git fetch left behind in the FETCH_HEAD file (with some special casing and other caveats that I won't go into here).

When your current branch is master but you instruct git pull to pull origin br1, it's the git merge step that brings master up to date with br1. More precisely, the merge gets you up to date with your copy of origin/br1 as of the time the git fetch finished—it's possible that just after your git fetch finished, someone else did a git push that updated br1 on your remote.

The merge is a "fast-forward" merge if possible, but again I won't go into any more details on that here. I'll just note that it was possible, so it was done; that's the Fast-forward line in the update.

In any case, the merge brought in to your current branch (master) the changes since the merge-base of your current branch and your target commit (the raw SHA-1 that git fetch left in the FETCH_HEAD file, which is also the new SHA-1 of origin/br1, and in one case, the new SHA-1 of new-or-updated local branch br1).

In pre-1.8.4 versions of git, the origin/br1 remote-tracking branch is not updated. Everything still works out of the FETCH_HEAD file though, and it is, if anything, even more confusing than in newer gits, where we can say that you're now up to date with origin/br1 without having to be very exacting and picky about "br1 as it was on the remote at the time you ran git fetch".

What's that plus sign?

Sharp-eyed readers will have noted the + in +refs/heads/*:refs/remotes/origin/*. This + symbol means "forced update". Normally, when updating a branch reference—any reference starting with refs/heads/—git won't allow the update unless it's a "fast-forward" label update. Setting the force flag in a refspec allows that particular update. Using --force on the command line also allows that update, and all other reference updates as well. In other words, the plus sign is simply a more-targeted (single refspec) version of --force.


1This is an overstatement: sometimes it uses a three-argument git fetch.

2Always true for the remote bit but the refspec bit may be empty, with the pull script figuring out which refspec to apply later, after git fetch finishes.

3By default, the fetch operation will also bring over any tag-name references that match any commit-IDs that it brings over. If you run git fetch yourself, you can change the way fetch handles these, but if you just let git pull run git fetch you will get this default behavior. Note that it's your local git that makes these decisions: the remote git simply displays everything to your local git, and then your git decides whether to add tags to your repository.

4Technically git fetch just invokes the C code that does this, rather than actually running git config --get-all. In any case, if there are multiple configuration entries for remote.origin.fetch, git fetch origin really does apply all of them. The way it does this is a bit complicated though, and I will skip the gory details here.

5Remote-tracking branches are really just references whose name starts with refs/remotes/, just as local branches are references whose name starts with refs/heads/. This is a general thing in git: your tags are references whose name starts with refs/tags. The git stash script uses a single special reference, refs/stash. Git's "notes" are stored under refs/notes/, and you can invent your own references: just pick a different starting string, and hope that nobody else picks the same one in the future for a new git feature. :-)

VonC

Any line with origin/xxxx means the git fetch part of a git pull (which is git fetch + git merge) has updated a remote tracking branch.
that is so since git 1.8.4 (before it only updated FETCH_HEAD).

If I do just a "git pull", it will pull remote br1 into local master

That would depend on git config branch.b1.merge, which instruct the git merge part of the git pull where to merge.
By doing a git pull b1:b1, you are overriding that config and instructing the merge to take place in the local b1 branch.

See also the result of git branch -avvv to see what local branch is tracking what remote branch.

I'm expecting it also shows something like "br1 -> master". Why it does not show that?

Maybe master was already at 97d4825.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!