I am having a hard time understanding the nuances of git-fetch. I understand that doing a fetch
, fetches the remote refs into a local tracking branch.
I
felipec have answered most of issues in question in his answer.
A few remaining (most taken from git fetch manpage; which is a bit dated in some places, unfortunately):
If remote-tracking branch (branch which tracks some branch in some remote repository) does not exists, it would be created.
The branch you fetch into (the <dst>
in [+]<src>:<dst>
) doesn't need to reside in remotes/<remote>/
namespace. For example for mirroring repositories (git clone --mirror
) refspec is 1 to 1. In old days before separate remotes layout (before remotes/<remote>/
namespace for remote-tracking refs) master branch was fetched into branch called origin. Even currently tags are fetched directly into tags/
namespace in mirroring fashion.
If branch you are fetching into (the right hand side of refspec <src>:<dst>
does exist, Git would check if download would result in fast-forward, i.e. if current state in <dst>
is ancestor of state in <src>
in given remote repository. If it isn't, and you don't use -f
/--force
option to git-fetch, or prefix refspec with '+' (use +<src>:<dst>
refspec) fetch would refuse to update that branch.
git fetch origin master
is equivalent to git fetch origin master:
, not to git fetch origin master:master
; it stores fetched value of master branch (of remote origin) in FETCH_HEAD, and not in master branch or remote-tracking remotes/origin/master
branch. It can be followed by git merge FETCH_HEAD
. Usually not used directly, but as part of one-time pull without setting remote-tracking branch: git pull <URL> <branch>
.
+refs/heads/*:refs/remotes/origin/*
as value for remote.origin.fetch configuration variable means that each branch (ref in refs/heads/
namespace) in remote origin is fetched into respectively named remote-tracking branch in refs/remotes/origin/
namespace, e.g. master branch in origin (i.e. refs/heads/master
ref) would be fetched into origin/master remote-tracking branch (i.e. refs/remotes/origin/master
ref). The '+' prefix means that fetch would succeed even in non fast-forward case, which means when branch on remote is rebased, or rewound (reset to some state in past) or otherwise amended.
Sidenote: You would probably want to use higher level git remote command to manage remote repositories and get updates.
Note that the main maintainer for Git has now (Git 2.1, August 2014) added this explanation for git fetch:
(See commit fcb14b0 by Junio C Hamano (gitster):
You often interact with the same remote repository by regularly and repeatedly fetching from it. In order to keep track of the progress of such a remote repository,
git fetch
allows you to configureremote.<repository>.fetch
configuration variables.Typically such a variable may look like this:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
This configuration is used in two ways:
When
git fetch
is run without specifying what branches and/or tags to fetch on the command line, e.g.git fetch origin
orgit fetch
,remote.<repository>.fetch
values are used as the refspecs---they specify which refs to fetch and which local refs to update.
The example above will fetch all branches that exist in theorigin
(i.e. any ref that matches the left-hand side of the value,refs/heads/*
) and update the corresponding remote-tracking branches in therefs/remotes/origin/*
hierarchy.When
git fetch
is run with explicit branches and/or tags to fetch on the command line, e.g.git fetch origin master
, the<refspec>
s given on the command line determine what are to be fetched (e.g.master
in the example, which is a short-hand formaster:
, which in turn means "fetch the 'master
' branch but I do not explicitly say what remote-tracking branch to update with it from the command line"), and the example command will fetch only the 'master
' branch.
Theremote.<repository>.fetch
values determine which remote-tracking branch, if any, is updated.
When used in this way, theremote.<repository>.fetch
values do not have any effect in deciding what gets fetched (i.e. the values are not used as refspecs when the command-line lists refspecs); they are only used to decide where the refs that are fetched are stored by acting as a mapping.
Note also that, with Git 2.5+ (Q2 2015), git merge FETCH_HEAD
can merge multiple git fetch's.
See commit d45366e by Junio C Hamano (gitster), 26 Mar 2015.
(Merged by Junio C Hamano -- gitster -- in commit bcd1ecd, 19 May 2015)
"
git merge FETCH_HEAD
" learned that the previous "git fetch
" could be to create an Octopus merge, i.e. recording multiple branches that are not marked as "not-for-merge";
this allows us to lose an old style invocation "git merge <msg> HEAD $commits...
" in the implementation of "git pull
" script; the old style syntax can now be deprecated.
The git merge doc now mention:
When
FETCH_HEAD
(and no other commit) is specified, the branches recorded in the.git/FETCH_HEAD
file by the previous invocation ofgit fetch
for merging are merged to the current branch.
Git 2.13 (Q2 2017) officially retires the old syntax for git merge
.
See commit b439165 (26 Mar 2015) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 1fdbfc4, 30 Mar 2017)
merge
: drop 'git merge <message> HEAD <commit>
' syntaxStop supporting "
git merge <message> HEAD <commit>
" syntax that has been deprecated since October 2007, and issues a deprecation warning message since v2.5.0.
That means the warning message old-style "'git merge <msg> HEAD <commit>' is deprecated.
" is no more.
First, there's no such concept of local tracking branches, only remote tracking branches. So origin/master is a remote tracking branch for master in the origin repo.
Typically you do git fetch $remote which updates all your remote tracking branches, and creates new ones if needed.
However, you can also specify a refspec, but that will not touch your remote tracking branches, instead, it will fetch the branch you specified and save it on FETCH_HEAD, unless you specify a destination. In general you don't want to mess with this.
Finally,
fetch = +refs/heads/*:refs/remotes/origin/*
That means if you do
git fetch origin
It will actually do:
git fetch origin +refs/heads/*:refs/remotes/origin/*
Which means a remote heads/foobar will be local remotes/origin/foobar, and the plus sign means they'll be updated even if they are not fast-forward.
Perhaps what you think as a tracking branch is something related to git pull and the merge config.