我是doing a fetch/merge,想知道做
之间是否有任何区别git fetch
和
git fetch origin master
我在GitHub上的remote repository
没有任何其他分支和原点。
当我这样做时:
git fetch origin master
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:XXXXXXXXXXXXXXX
* branch master -> FETCH_HEAD
但只是:
git fetch
From github.com:XXXXXXXXXXXXXXX
531d466..aaf6df0 master -> origin/master
注意主人指向不同的东西;在一个案例中FETCH_HEAD
,在其他情况下,origin/master
?
他们不一样吗?
答案 0 :(得分:6)
这里是" TL; DR"版本(隐藏在批次的特殊情况下):git fetch
总是更新FETCH_HEAD
,在各种情况下都有多行。它有时更新"远程分支",这些是全名以refs/remotes/
开头的引用。其余的主要是关于"有时",这取决于git fetch
和git版本的参数数量。
我有机会测试这个。让我们区分三种情况,所有这些情况都假定在没有git fetch
甚至-a
等额外选项的情况下运行--all
。我们还排除git fetch
的怪异变体,例如直接使用网址,insteadOf
条目或.git/remotes
或.git/branches
中列出的文件。 (我承认我只是在猜测,但我认为这些都是[remote "name"]
条目进入git配置文件前的剩余时间。编辑,2019:结果证明是正确的。)
git fetch
,没有其他参数。
Git确定您当前的分支(以通常的方式,通过阅读HEAD
,但您当然可以看到git branch
或git status
的内容。然后,它会查找该分支的配置条目,并将其命名为remote
。例如,假设您在分支dummy
和.git/config
上(以及其他条目):
[branch "dummy"]
remote = remote-X
在这种情况下,git fetch
相当于git fetch remote-X
。在那之后,这相当于案例2,即:
git fetch remote
(除此之外不再有其他论据)。
这次Git不看你当前的分支。要使用的遥控器是命令行上给出的遥控器。它 查找给定远程的配置部分。我们假设您正在使用remote-X
:在这种情况下,它会查找:
[remote "remote-X"]
url = ...
如果该部分不存在,或者没有url =
条目,则会收到错误:fatal: 'remote-X' does not appear to be a git repository
。 1 否则会提供URL,{{1将尝试连接到那里。假设它可以连接......
通常还有至少一个配置条目,可能更多,阅读:
git fetch
(遥控器的名称在这里是硬编码的)。假设有......
接下来, fetch = +refs/heads/*:refs/remotes/remote-X/*
向遥控器询问它有什么内容(主要是分支和标签,尽管你可以获得所有引用,但大多数人只关心分支和标签)。你可以使用git fetch
自己做同样的事情,这会溢出这样的东西:
git ls-remote remote-X
676699a0e0cdfd97521f3524c763222f1c30a094 HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch
676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master
ref的处理并不完全一致(我看到它表现得很奇怪),但通常在这里它会被丢弃。 2 其余的分支被重命名,根据{{1}} refspec更新。 (如果有多个HEAD
refspec,则会根据所有这些重新命名和更新。这主要用于引入fetch =
或制作自己的"远程标记&#34 ;例如,fetch =
下的名称空间。)
在这种情况下,fetch将带来两个分支refs/notes/
和refs/rtags/
所需的任何对象,并更新(本地)"远程分支"根据需要,名称branch
和master
。对于每个更新的版本,refs/remotes/remote-X/branch
会打印一行如下:
refs/remotes/remote-X/master
如果缺少fetch
行,您会得到完全不同的内容。输出将显示为:
22b38d1..676699a master -> remote-X/master
在这种情况下,它就像(缺少的)fetch =
行一样,包含 * branch HEAD -> FETCH_HEAD
。
fetch =
( fetch = HEAD
部分是一个或多个refspec,实际上,如下所述。)
这类似于案例2,只是这一次," refspecs"在命令行上提供,而不是从远程的git fetch remote refspec
配置条目提供。但是,这里的获取行为非常不同。
在这种特殊情况下,让我们暂停片刻并正确描述一个refspec。 (对于refspec
也会出现Refspec,但与git一样,实现细节会泄露出来,并且它们的工作方式略有不同。)refspec有一个可选的前导加号(fetch =
)符号,我可以使用它。 ll忽略这里; 3 然后是两个部分,用冒号(git push
)分隔。两者通常只是一个分支名称,但你可以(和+
行)拼出" full"在分支名称的情况下引用名称:
。
对于提取操作,左侧的名称是遥控器本身的名称(例如,如fetch =
所示)。右侧的名称是要在本地git存储库中存储/更新的名称。作为一种特殊情况,您可以在斜杠之后使用星号(refs/heads/branch
)作为最后一个组件,例如git ls-remote
,在这种情况下,左侧匹配的部分将替换为右侧。因此*
导致refs/heads/*
(在远程上看到,refs/heads/*:refs/remotes/remote-X/*
)成为refs/heads/master
的原因(如本地存储库中所示,并且以较短的形式显示在git ls-remote
上) refs/remotes/remote-X/master
行->
的右侧打印)。
如果你没有放入git fetch
,:
没有好的地方可以把#34;那个分支的副本放在那里"。让我们说它将带来遥控器git fetch
(遥控器上的refs/heads/master
分支)。而不是更新您的 master
- 如果您在分支refs/heads/master
中拥有自己的提交,那么这显然会很糟糕 - 它只是将更新转储到master
。
这里的事情特别棘手。我们假设您运行FETCH_HEAD
,即至少提供一个,甚至几个refspecs,但都没有冒号。
如果您的git版本低于1.8.4,则更新仅进入git fetch remote-X master branch
。如果您提供了两个无冒号的refspec,FETCH_HEAD
现在包含两个行:
FETCH_HEAD
如果你的git版本是1.8.4或更新版本,那么更新就会在那里 - 这部分没有改变 - 但也,fetch有机会永久记录这些分支 在其正确的远程分支中,由远程的676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ...
222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ...
行给出。
无论出于何种原因,fetch =
仅为实际更新的远程分支打印出更新git fetch
行。由于总是记录->
中的所有更新,因此始终会在此处打印分支名称。
(另一个问题,除了需要git 1.8.4或更新版本,更新远程分支是必须存在那些FETCH_HEAD
行。如果他们没有,那么就没有映射fetch知道将fetch =
重命名为refs/heads/*
。)
换句话说,git 1.8.4和更新的确实会机会性地更新"所有的远程分支机构。较旧版本的git在refs/remotes/remote-X/*
上执行,因此它之前一直不一致。即使在git 1.8.4中它仍然与git push
不一致,我认为(尽管我不会git pull
使用足够的注意:-));应该在git 1.9中修复。
现在让我们回到git pull
和git fetch remote
之间的差异。
如果您运行git fetch remote refspec ...
,即省略所有refspecs,则提取会像往常一样回退到git fetch remote
行。获取操作会带来fetch =
行的所有引用。 所有这些都会进入fetch
,但这次他们会标记为" not-for-merge" (使用标签,我更改为一个空格以更好地适应网页):
FETCH_HEAD
不是分支的引用,例如676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
引用的引用,而是读取:
refs/notes/
同时,如有必要,会更新远程分支引用,并显示消息,告知您哪些已更新:
f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
同样,所有都会被转储到 22b38d1..676699a master -> remote-X/master
,但只会引用"需要更新"更新和打印。新的分支机构获得了新的分支机构"印刷版和旧版SHA-1都印有缩写的新旧SHA-1,与上面的FETCH_HEAD
一样。
另一方面,如果您运行master -> remote-X/master
,则提取只会带来 指定的refspecs。这些 all 像往常一样进入git fetch remote refspec ...
, 6 ,但这一次都会打印出来。然后,如果你的git是1.8.4或更高版本,任何可以映射的参考更新(通过合理的FETCH_HEAD
行)并且需要更新也更新并打印:
fetch =
如果您的git版本低于1.8.4,则在这种情况下不会发生 * branch master -> FETCH_HEAD
* branch branch -> FETCH_HEAD
22b38d1..676699a master -> remote-X/master
的更新 - 或者更确切地说,除非您的某个命令行refspec是{{1},否则不会发生更新},或remote-X/master
,或前面带加号的那些变体。
1 这不是一个很好的错误消息。 refs/heads/master:refs/remotes/remote-X/master
参数从来不应该是"存储库"它应该是一个"远程"!如果git在这里说了更多信息,那可能会很好。
2 git远程协议存在缺陷:HEAD通常是间接引用,因为它是远程协议上的当前分支,所以它应该以&#结束34; ref:refs / heads / master"例如,它取而代之的是完全解析的SHA-1。至少有一个git命令(refs/heads/*:refs/remotes/remote-X/*
)尝试"猜测"通过将此SHA-1与每个分支头的SHA-1进行比较,可以获得远程控制器上的当前分支。例如,在上面,很清楚遥控器是分支机构",因为remote-X
和git clone
具有相同的SHA-1。但是,如果多个分支名称指向同一个提交,并且HEAD
匹配该提交ID,则无法确定哪个分支(如果有)refs/heads/master
处于打开状态。遥控器可以在"分离的HEAD"状态也是如此,在这种情况下它不在任何分支上,无论SHA-1值如何。
编辑,2019:这个错误在Git版本1.8.4.3中得到修复。只要你在自己机器上克隆的机器上的两个Git版本都是1.8.4.3或更新版本,Git就不用再猜测了。
3 加号意味着"接受强制更新",即采取将被"拒绝的更新除了快进" 分支的4 规则,或者"永远不会为标签更改标签" 5 。
4 A"快进"对于标签,当提交指向非循环图中的旧SHA-1是新SHA-1的祖先时,可以将其从旧的SHA-1更改为新的SHA-1。
5 "永远不会改变标签"规则在git 1.8.2中是新的。如果你的git比那个旧,那么git也会使用标签的分支规则,允许快速转发而不会强制更新"。
6 但这次没有HEAD
。基本上,当你提供无冒号的refspecs时,HEAD
假设他们已经"对于合并"并将它们放入not-for-merge
,以便git fetch
可以找到它们。 (我还没有测试非分支引用会发生什么。)