Reputation: 103
I have been playing around with git and stuck with concepts of git fetch.
I have tried and observed following scenarios:
1)
$ git fetch
All the remote branches are fetched and all the corresponding remote-tracking branches are updated. I understand that it looks at .git/config file and uses default remote repository location and default fetch refspec which is generally "refs/heads/:refs/remotes/origin/". It does not fast forward any local branches.
2)
$ git fetch origin
Observed same as 1)
3)
$ git fetch origin master
Only master branch is fetched and remote-tracking branch origin/master is updated. Does not fast forward the local 'master' branch.
4)
$ git fetch origin master:master
Only master branch is fetched and remote-tracking branch origin/master is updated. Fast forwards the local 'master' branch.
Understanding 1) and 2) was straightforward. However 3) and 4) confuses me.
What does a fetch command had to do anything with local branches? Why does it fast forward the local branch? (Proof that it affects the local branch is when I try git fetch origin master:master
, while the master branch is checked out, it throws the following error: fatal: Refusing to fetch into current branch refs/heads/master of non-bare repository
)
According to my intuition, on any type of fetch command, updating the remote-tracking branches is default behavior. However when git fetch origin master
is executed, it has an additional behavior of updating FETCH_HEAD. When git fetch origin master:master
is executed, it has an additional behavior of updating local branch 'master'. Is my intuition right?
Does it interpret git fetch origin master:master
as git fetch origin refs/heads/master:refs/heads/master
? (Maybe redundant to my previous question)
Does it interpret git fetch origin master
as git fetch origin refs/heads/master:
?
Upvotes: 5
Views: 2054
Reputation: 489848
RomainValeri's answer is mostly right but misses a few items.
First, we have to make note of a special case about Git versions that predate 1.8.4. The 1.8.4 release notes mention that
- "git fetch origin master" unlike "git fetch origin" or "git fetch" did not update "refs/remotes/origin/master"; this was an early design decision to keep the update of remote tracking branches predictable, but in practice it turns out that people find it more convenient to opportunistically update them whenever we have a chance, and we have been updating them when we run "git push" which already breaks the original "predictability" anyway.
Hence:
git fetch origin master
behaves differently in Git 1.8.3 and earlier. In 1.8.4 and later, this updates refs/remotes/origin/master
(by default—but it depends on your default fetch refspecs).
The description of a refspec is correct but missing one item: the syntax in general is:
+
indicating --force
(for this particular refspec only);:
; andBoth source and destination can be fully qualified, e.g., refs/heads/master
, or not, e.g., master
. Git will figure out, if it can, the fully qualified spelling if you use an unqualified variant.
In general, either (or sometimes even both) of the source and destination can be omitted. The meaning of such a refspec is different for git fetch
and git push
. Omitting the source requires including a colon, while omitting the destination allows you to omit the colon too. That is:
master:master
, or branch1:branch2
, or tag:tag
, etc., all provide both source and destination.master
is a source without a destination.master:
is also a source without a destination.:master
is a destination without a source. This is valid only for git push
, where it means ask the other Git to delete their reference.:
indicates that both source and destination are omitted. This is valid only for git push
, where it means find matching branch names and push those.Ignoring git push
, we then allow only source-and-destination, as in master:origin/master
or master:master
, or source-without-destination, as in master
or master:
. So there are only two cases:
You provided a source but no destination. git fetch
will not update any names, except that Git since 1.8.4 will opportunistically update any remote-tracking names as specified in the default fetch refspec. So if your Git is at least 1.8.4, git fetch origin master
updates your origin/master
, which is your Git's remote-tracking name for origin
's Git's master
.
You provided both a source and a destination. git fetch
will attempt to update the destination name. If you did not set a force flag—either the global one or the leading +
—this update is required to be a fast-forward. If you did not specify --update-head-ok
, the destination must not be the current branch either.1 In any case, if your Git is 1.8.4 or newer, your Git will opportunistically update the remote-tracking name for the source name.
Note that:
git fetch origin
(which specifies no refspecs on the command line) will use the default refspecs from your configuration. Assuming this is origin
, see git config --get-all remote.origin.fetch
to see those default refspecs.
1In what I think amounts to a bug, the test here, for branch is checked-out, tests only the main work-tree's HEAD
. If the branch is currently checked out in an added work-tree, the added work-tree's index and work-tree are no longer in sync with its branch. See also Why does Git allow pushing to a checked-out branch in an added worktree? How shall I recover?—the bug is symmetric across fetch and push.
Upvotes: 5
Reputation: 22067
Updating the matching local ref is the expected behaviour when you explictly mention the destination in the <refspec>
.
With the format :
git fetch [options] <remote> <refspec>
where refspec
takes the form
<src>:<dst>
(obviously, for "source" and "destination")
it's said in the manual that
The remote ref that matches
<src>
is fetched, and if<dst>
is not an empty string, an attempt is made to update the local ref that matches it.
You can also take a look at this paragraph above this specific syntax.
And to answer the side questions :
However when
git fetch origin master
is executed, it has an additional behavior of updatingFETCH_HEAD
. Whengit fetch origin master:master
is executed, it has an additional behavior of updating local branch 'master'. Is my intuition right?
Not exactly. If the <refspec>
is given not as <src>:<dst>
, the given name is taken as the <src>
, so when you did git fetch origin master
it's the same as git fetch origin master:master
. FETCH_HEAD
is used for internals, namely those of git pull
, and is available for scripting, but you don't need to bother in everyday git use.
"Does it interpret git
fetch origin master:master
asgit fetch origin refs/heads/master:refs/heads/master
? (Maybe redundant to my previous question)"
Yes.
Does it interpret
git fetch origin master
asgit fetch origin refs/heads/master:
?
No, if the <dst>
side of the <refspec>
is empty, nothing is updated at all since you're pointing to nothing. (not to be confused with the case where you omit the :
itself, mentioned above. Anecdotically, the reverse :my-branch
is deleting my-branch
since it's updated with a void ref)
Upvotes: 3