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 mean