问题
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 have a few questions though:
Can it be possible that a local tracking branch does not exist? If so, will it then be created automatically?
What will happen if I do a
fetch
and specify a non tracking branch as the destination?The man page for git-fetch specifies:
git-fetch <options> <repository> <refspec>
How would I use the refspec to fetch contents from my remote master into its remote tracking branch? I believe this may be possible if my current HEAD is on master and I run
git fetch origin master
However, can I use the <+?src:dest>
refspec to achieve the same thing? I think this will help me understand the concepts better.
And one more question:
My .git/config file has the following line for fetching (showing only relevant lines):
fetch = +refs/heads/*:refs/remotes/origin/*
Can someone please explain what this line exactly means?
回答1:
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.
回答2:
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 inremotes/<remote>/
namespace. For example for mirroring repositories (git clone --mirror
) refspec is 1 to 1. In old days before separate remotes layout (beforeremotes/<remote>/
namespace for remote-tracking refs) master branch was fetched into branch called origin. Even currently tags are fetched directly intotags/
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 togit fetch origin master:
, not togit fetch origin master:master
; it stores fetched value of master branch (of remote origin) in FETCH_HEAD, and not in master branch or remote-trackingremotes/origin/master
branch. It can be followed bygit 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 inrefs/heads/
namespace) in remote origin is fetched into respectively named remote-tracking branch inrefs/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.
回答3:
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):
CONFIGURED REMOTE-TRACKING BRANCHES
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.
回答4:
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.
来源:https://stackoverflow.com/questions/1070496/having-a-hard-time-understanding-git-fetch