这是“TL;DR”版本(它掩盖了lot特殊情况):git fetch
always更新FETCH_HEAD
,在各种情况下都有不止一条线。它有时更新“远程分支”,它们是全名开头的引用refs/remotes/
。其余的主要是关于“有时”,它根据给出的参数数量而变化git fetch
,以及 git 版本。
我有机会测试一下。让我们区分三种情况,所有这些都假设正在运行git fetch
没有额外的选项,例如-a
甚至--all
。我们还排除更奇怪的变体git fetch
,就像直接使用 URL 一样,或者insteadOf
条目或列出的文件.git/remotes
or .git/branches
。 (我承认我只是猜测,但我认为这些都是前几天留下的[remote "name"]
条目进入 git 的配置文件。 2019 年编辑:事实证明这是正确的。)
-
git fetch
,并且没有其他参数。
Git 确定您当前的分支(以通常的方式,通过阅读HEAD
,但你当然可以看到它是什么git branch
or git status
)。然后它会查找该分支的配置条目,并将其命名为remote
。例如,假设您在分支机构dummy
and .git/config
有(以及其他条目):
[branch "dummy"]
remote = remote-X
在这种情况下git fetch
相当于git fetch remote-X
。之后,这相当于情况 2,即:
-
git fetch remote
(除此之外没有更多的争论)。
Git 这次不会查看你当前的分支。要使用的遥控器是命令行上给出的遥控器。它does查找给定遥控器的配置部分。假设你正在使用remote-X
:在这种情况下,它会查找:
[remote "remote-X"]
url = ...
If that section does not exist, or there is no url =
entry, you get an error: fatal: 'remote-X' does not appear to be a git repository
.1 Otherwise that gives the URL, and git fetch
will attempt to connect to there. Assuming it can connect...
通常还有至少一个配置条目,可能还有更多,内容如下:
fetch = +refs/heads/*:refs/remotes/remote-X/*
(遥控器的名称在这里是硬编码的)。假设有...
Next, git fetch
询问遥控器它有什么引用(主要是分支和标签,虽然你可以获得所有引用,但大多数人只关心分支和标签)。你可以自己做同样的事情git ls-remote remote-X
,它会溢出这样的东西:
676699a0e0cdfd97521f3524c763222f1c30a094 HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch
676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master
The treatment of the HEAD
ref is not entirely consistent (I've seen it behave oddly) but usually here it just gets dropped.2 The remaining branches are renamed and updated according to the fetch =
refspec. (If there are multiple fetch =
refspecs, they're renamed and updated according to all of them. This is mainly useful for bringing over refs/notes/
or making your own "remote tags" name-space under refs/rtags/
, for instance.)
在这种情况下,fetch 将带来两个分支所需的任何对象branch
and master
,并更新(本地)“远程分支”名称,refs/remotes/remote-X/branch
and refs/remotes/remote-X/master
, 如所须。对于每一个更新的内容,fetch
打印这样一行:
22b38d1..676699a master -> remote-X/master
If the fetch =
线路缺失,你会得到完全不同的东西。输出将显示:
* branch HEAD -> FETCH_HEAD
在这种情况下,就好像(缺失)fetch =
线路在那里并且被包含fetch = HEAD
.
-
git fetch remote refspec
(the refspec
部分实际上是一个或多个参考规范,如下所述)。
这与情况 2 类似,只是这一次,“refspecs”是在命令行上提供的,而不是从fetch =
远程的配置条目。然而,这里的获取行为有很大不同。
Let's pause a moment and describe a refspec properly, in this particular case. (Refspecs also occur for git push
but, as usual with git, implementation details leak out and they work slightly differently there.) A refspec has an optional leading plus (+
) sign, which I'll ignore here;3 then two parts, separated by a colon (:
). Both are often just a branch name, but you can (and fetch =
lines do) spell out the "full" ref-name, refs/heads/branch
in the case of a branch name.
对于获取操作,左侧的名称是远程本身的名称(如git ls-remote
例如)。右侧的名称是要在本地 git 存储库中存储/更新的名称。作为特殊情况,您可以有一个星号(*
) 在斜线之后作为最后一个组成部分,例如refs/heads/*
,在这种情况下,左侧匹配的部分将被右侧替换。因此refs/heads/*:refs/remotes/remote-X/*
是什么导致refs/heads/master
(如在遥控器上看到的,git ls-remote
) 成为refs/remotes/remote-X/master
(如您的本地存储库中所示,以较短的形式显示在->
line git fetch
印刷)。
如果你不输入:
, 尽管,git fetch
没有好地方可以放置“那边的分支”的副本。假设它将带来遥控器的refs/heads/master
(the master
远程分支)。而不是更新your refs/heads/master
——如果你在分支中有自己的提交,显然那会很糟糕master
-它只是将更新转储到FETCH_HEAD
.
这就是事情变得特别奇怪的地方。假设你跑git fetch remote-X master branch
,即给出至少一个,也许几个,refspec,但都没有冒号。
-
如果您的 git 版本早于 1.8.4,则更新only进入FETCH_HEAD
。如果你给出了两个无冒号的参考规范,FETCH_HEAD
现在包含two lines:
676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ...
222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ...
-
如果您的 git 版本是 1.8.4 或更高版本,则更新会在那里 — 这部分不变 — 但是also,fetch趁机记录这些分支永久在其适当的远程分支中,如给出的fetch =
遥控器的线路。
但无论出于什么原因,git fetch
只打印出更新->
实际更新的远程分支的行。自从它always记录所有更新FETCH_HEAD
, it always在这里打印分支名称。
(除了需要 git 1.8.4 或更新版本之外,更新远程分支的另一个问题是那些fetch =
线路必须存在。如果不这样做,则没有映射可以让 fetch 知道重命名refs/heads/*
to refs/remotes/remote-X/*
.)
换句话说,git 1.8.4 及更新版本确实“伺机更新”所有远程分支。旧版本的 git 可以在git push
,所以之前一直不一致。即使在 git 1.8.4 中它仍然与git pull
,我认为(虽然我不使用git pull
足以引起注意:-) );这应该在 git 1.9 中得到修复。
现在让我们回到两者之间的区别git fetch remote
and git fetch remote refspec ...
.
-
如果你跑git fetch remote
,即省略所有 refspec,获取会回退到fetch =
线路如常。获取操作带来了来自的所有引用fetch
线。All这些进入FETCH_HEAD
,但这一次它们被标记为“不可合并”(带有制表符,我将其更改为一个空格以更好地适应网页):
676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
不是分支的引用,例如,refs/notes/
带来的参考文献,请改为阅读:
f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
同时,如有必要,远程分支引用也会更新,并有消息告诉您哪些分支已更新:
22b38d1..676699a master -> remote-X/master
Again, 一切被倾倒到FETCH_HEAD
,但仅更新和打印“需要更新”的参考。新分支打印“新分支”,旧分支打印缩写的新旧 SHA-1,至于master -> remote-X/master
above.
-
If, on the other hand, you run git fetch remote refspec ...
, the fetch brings over only the specified refspecs. These all go into FETCH_HEAD
as usual,6 but this time every one of them is printed. Then, if your git is 1.8.4 or newer, any reference-updates that can be mapped (via sensible fetch =
lines) and need updating are also updated and printed:
* branch master -> FETCH_HEAD
* branch branch -> FETCH_HEAD
22b38d1..676699a master -> remote-X/master
如果您的 git 版本早于 1.8.4,则更新remote-X/master
在这种情况下不会发生,或者更确切地说,除非您的命令行参考规范之一是,否则不会发生refs/heads/master:refs/remotes/remote-X/master
, or refs/heads/*:refs/remotes/remote-X/*
,或前面带有加号的变体。
1This is not a great error message. The remote-X
argument was never supposed to be a "repository", it was supposed to be a "remote"! It might be nice if git said something more informative here.
2There's a flaw in the git remote protocol: HEAD is usually an indirect ref as it's the current branch on the remote, so it should come over as "ref: refs/heads/master" for instance, but instead it comes over as the fully resolved SHA-1. At least one git command (git clone
) attempts to "guess" the current branch on the remote by comparing this SHA-1 to that of each branch-head. In the above, for instance, it's clear that the remote is "on branch master", as HEAD
and refs/heads/master
have the same SHA-1. But if multiple branch names point to the same commit, and HEAD
matches that commit-ID, there's no way to tell which branch (if any) HEAD
is on. The remote could be in "detached HEAD" state too, in which case it's not on any branch, regardless of SHA-1 values.
2019 年编辑:此错误已在 Git 版本 1.8.4.3 中修复。只要您克隆的计算机和您自己的计算机上的两个 Git 版本都是 1.8.4.3 或更高版本,Git 就不再需要猜测。
3The plus sign means "accept forced updates", i.e., take updates that would be rejected by the "nothing but fast forward"4 rule for branches, or "never change tags"5 for tags.
4A "fast forward" for a label, changing it from an old SHA-1 to a new one, is possible when the old SHA-1 in the commit Directed Acyclic Graph is an ancestor of the new SHA-1.
5The "never change tags" rule was new in git 1.8.2. If your git is older than that, git uses the branch rules for tags too, allowing fast-forwarding without "forced update".
6But without the not-for-merge
this time. Basically, when you supply colon-less refspecs, git fetch
assumes they're "for merge" and puts them into FETCH_HEAD
so that git merge FETCH_HEAD
can find them. (I have not tested what happens with non-branch refs.)