Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

git pull command output message meaning into which branch

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

  • Command 1: If I do a "git pull origin br1:br1" it pulls remote br1 into local br1, and shows: 9188a5d..97d4825 br1 -> br1 9188a5d..97d4825 br1 -> origin/br1

  • command 2: If I do just a "git pull", it will pull remote br1 into local master, but it shows only the following: 9188a5d..97d4825 br1 -> origin/br1

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

Does "br1 -> br1" mean pulling remote br1 into local br1?

What does that "br1 -> origin/br1" mean?

Update: With help from VonC, I figured out the following:

  • git pull updates all the tracking branches. br1 -> origin/br1 means br1 on the remote is pulled into local tracking branch origin/br1.

  • git pull origin br1:br1 pulls remote br1 into local br1 and into origin/br1. Then the messages after that mean the same change set is also pulled into the current checked-out branch (the message is Updating ..., it does not show br1 -> master): $ git pull origin br1:br1 beb48a4..b344dd1 br1 -> br1 beb48a4..b344dd1 br1 -> origin/br1 Updating cca5a9b..b344dd1 Fast-forward file2 | 0 file3 | 0 4 files changed, 0 insertions(+), 0 deletions(-)

  • Previously I thought git pull pulls into the local master. That is not the case. It is that git pull origin br1:br1 does it.

Update 2: With explanations by torek, the specific problem is that the command git pull origin br1:br1 pulls remote br1 into FETCH_HEAD after a sequence of other actions and subsequently merges the FETCH_HEAD onto the current branch.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...