Candic3 Candic3 - 2 months ago 24
Git Question

Using colon in `git pull origin <remote-branch> : <local-branch>` does not pull to correct branch?

Let's say I am on a different branch than

<local-branch>
, called
<different-branch>
.

When I try to pull from a remote branch to a local branch, I do the following:

git pull origin <remote-branch>:<local-branch>


And, from my understanding this should pull into my
<local-branch>
, and not pull into
<different-branch>
.

But, then when I check
git log
while I'm on
<different-branch>
, I see that those commits are from
<remote-branch>
?

How do I pull from a remote branch, into a local branch, when the local branch is different from the one that I cam currently on? Thank you.

Answer

Using git pull with a refspec will not affect the merging part of the pull command. As you maybe know, git pull is essentially just a combination of git fetch and git merge; first, it will fetch the newest changes from the remote and update the remote-tracking branch, and then it will merge that remote-tracking branch into the current branch.

Now, as I said, the refspec does not affect the merging part, but it only affects the fetching inside git pull. Now to understand what this eventually means, you first have to understand what a refspec is.

A refspec is basically just a configuration which remote branches map to which remote-tracking branch. The remote branches are here the branches that actually exist on the remote, and the remote-tracking branches are the branches that are created to track the state of the remote branches; for a remote called “origin”, its remote-tracking branches all start with origin/.

If you don’t specify a refspec explicitely, it is taken from the configuration file. The default form usually looks like this:

+refs/heads/*:refs/remotes/origin/*

This tells Git to fetch the remote branches located at refs/heads/* and map them to remote-tracking branches located at refs/remotes/origin/*. So for a remote branch master, refs/heads/master will map to refs/remotes/origin/master. The leading + also tells Git to overwrite the remote-tracking branch regardless of whether the changes could be fast-forwarded or not: After all, you usually want your remote-tracking branches to match exactly the state on the remote, so if the history is rewritten there (which should be avoided) or branches are renamed, you would want the remote-tracking branches to still respect that.

Now, when you specify the refspec (using git fetch or git pull), the default mapping is overridden. Instead, your mapping is used. For example, when you use git fetch origin master:foo, then a local branch foo is fast-forwarded (if possible) to point to the remote branch master. So this is actually a fine way to update a local branch, without having to check it out: If you leave out the leading +, then updating the local ref (branch) will fail if it’s not a fast-forward merge, so you’re also safe against conflicts.

But coming back to git pull—what happened when you ran the command? As I said, a pull is just a fetch and a merge, so your git pull command first did this:

git fetch origin <remote-branch>:<local-branch>

So the remote branch is fetched from the remote, and the local branch is updated—if it’s a fast-forward merge. This already does exactly what you wanted: Update <local-branch>.

But then, the merge part of git pull happens; and Git usually runs git merge FETCH_HEAD for this purpose. FETCH_HEAD is a reference to the last fetched branches. In this case, it points at <local-branch>. So after fetching into <local-branch>, the command that is being executed is git merge <local-branch>. And running git merge will merge into the current branch.

So when you’re on <different-branch> and run git pull origin <remote-branch>:<local-branch> then you will correctly update <local-branch> to match the remote branch, but you will then also merge those changes into the current branch, <different-branch>. That’s why you see the changes of that branch in the log of the current branch; they were simply merged.

If you want to avoid that, as per my explanations above, just use git fetch with the refspec. It will already update the local branch correctly (if it can) without affecting the current branch.