我正在进行获取/合并,并想知道做之间是否有任何区别
git fetch
和
git fetch origin master
我remote repository
在GitHub上没有任何其他分支和原点.
当我做:
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
?他们不一样吗?
这是"TL; DR"版本(它掩盖了很多特殊情况):git fetch
总是更新FETCH_HEAD
,在不同的情况下有多行.它有时会更新"远程分支",这些分支是全名开头的引用refs/remotes/
.剩下的主要是"有时",它根据给出的参数数量git fetch
和git版本而有所不同.
我有机会测试这个.让我们区分三种情况,所有情况都假定在git fetch
没有额外选项的情况下运行,-a
甚至是--all
.让我们也排除的怪异变种git fetch
,如直接使用URL,或者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,并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
对HEAD
ref 的处理并不完全一致(我已经看到它表现得很奇怪)但通常在这里它会被丢弃.2 其余分支根据fetch =
refspec 重命名和更新.(如果有多个fetch =
refspec,它们会根据所有这些重新命名和更新.例如,这主要用于将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
.
git fetch remote refspec
(该refspec
部分是一个或多个refspecs,实际上,如下所述).
这与情况2类似,只是这次,"refspecs"在命令行上提供,而不是从fetch =
远程的配置条目提供.但是,这里的获取行为非常不同.
在这种特殊情况下,让我们暂停片刻并正确描述refspec.(Refspec也会出现git push
但是,和git一样,实现细节会泄露出来,并且它们的工作方式略有不同.)refspec有一个可选的前导加号(+
)符号,我在这里忽略它; 3然后两个部分,用冒号(:
)分隔.两者通常只是一个分支名称,但在分支名称的情况下,您可以(和fetch =
行)拼出"完整"引用名称.refs/heads/branch
对于获取操作,左侧的名称是远程本身的名称(例如,如图所示git ls-remote
).右侧的名称是要在本地git存储库中存储/更新的名称.作为一种特殊情况,您可以*
在斜杠之后使用星号()作为最后一个组件,例如refs/heads/*
,在这种情况下,左侧匹配的部分将替换为右侧.因此refs/heads/*:refs/remotes/remote-X/*
导致refs/heads/master
(如在遥控器上看到的那样git ls-remote
)refs/remotes/remote-X/master
(在本地存储库中看到,并以较短的形式,->
在行的右侧git fetch
打印).
如果你不把在做:
,不过,git fetch
有没有好的地方放的副本"的分支那边".让我们说它将带来遥控器refs/heads/master
(遥控器上的master
分支).如果您在分支中拥有自己的提交,而不是更新您 - refs/heads/master
显然会很糟糕master
- 只需将更新转储到FETCH_HEAD
.
在这里,事情变得尤为突出.假设你运行git fetch remote-X master branch
,即给出至少一个,也许几个refspecs,但都没有冒号.
如果你的git版本低于1.8.4,那么更新只会进入FETCH_HEAD
.如果你给了两个无冒号的refspecs,FETCH_HEAD
现在包含两行:
676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ... 222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ...
如果你的Git版本是1.8.4或更高版本,更新去那里,这部分是不变的,但是也,抓取借此机会来记录这些部门永久,以正确的远程分支机构通过给出fetch =
的远程线路.
但是,无论出于何种原因,git fetch
只打印出->
实际更新的远程分支的更新行.由于它始终记录所有更新FETCH_HEAD
,因此它始终在此处打印分支名称.
(另一个问题,除了需要git的1.8.4或更高版本,以获得更新的远程分支机构是那些fetch =
必须存在线.如果他们不这样做,有没有映射,通过该取知道重命名refs/heads/*
到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
git fetch remote refspec ...
如果你运行,即省略所有refspecs,fetch会像往常一样回到行.获取操作将从线路中获取所有引用. 所有这些都进入了,但是这次它们被标记为"not-for-merge"(带有标签,我将其更改为一个空格以更好地适应网页):git fetch remote
fetch =
fetch
FETCH_HEAD
676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
不是分支的refs/notes/
引用,例如引用的引用,而是读取:
f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
同时,如有必要,可以更新远程分支引用,并通过消息告知您哪些已更新:
22b38d1..676699a master -> remote-X/master
同样,所有内容都被转储FETCH_HEAD
,但只是更新和打印"需要更新"的内容.新的分支机构打印出"新分支",旧的分支机构打印出新的SHA-1缩写,master -> remote-X/master
如上所述.
另一方面,如果你运行,fetch 只会带来指定的refspecs.这些所有进入像往常一样,6但这一次他们每个人都被打印出来.然后,如果你的git是1.8.4或更新,那么任何可以映射(通过合理的行)并需要更新的引用更新也会更新和打印:git fetch remote refspec ...
FETCH_HEAD
fetch =
* branch master -> FETCH_HEAD * branch branch -> FETCH_HEAD 22b38d1..676699a master -> remote-X/master
如果你的git版本早于1.8.4,那么remote-X/master
这种情况下不会发生更新- 或者更确切地说,除非你的命令行refspec之一是refs/heads/master:refs/remotes/remote-X/master
,或者refs/heads/*:refs/remotes/remote-X/*
是那些带有加号的变体,否则它不会发生.在前面的迹象.
1这不是一个很好的错误信息.这个remote-X
论点从来不应该是一个"存储库",它应该是一个"远程"!如果git在这里说了更多信息,那可能会很好.
2 git远程协议中存在一个缺陷:HEAD通常是间接引用,因为它是远程的当前分支,所以它应该以"ref:refs/heads/master"为例,但它取而代之的是完全解决了SHA-1.至少有一个git命令(git clone
)尝试通过将此SHA-1与每个分支头的SHA-1进行比较来"猜测"远程上的当前分支.例如,在上面,很明显遥控器是"在分支主机上",HEAD
并且refs/heads/master
具有相同的SHA-1.但是,如果多个分支名称指向同一个提交,并且HEAD
匹配该commit-ID,则无法确定哪个分支(如果有)HEAD
已打开.远程也可能处于"分离的HEAD"状态,在这种情况下,它不在任何分支上,无论SHA-1值如何.
编辑,2019:在Git版本1.8.4.3中修复了此错误.只要你在克隆的机器上和你自己的机器上的两个Git版本都是1.8.4.3或更新版本,Git再也不用猜测了.
3加号表示"接受强制更新",即对于分支采用"只有快进" 4规则拒绝的更新,或者对标签"永不更改标签" 5.
4当提交定向非循环图中的旧SHA-1是新SHA-1的祖先时,标签的"快进"可以从旧的SHA-1更改为新的SHA-1.
5 "永不改变标签"规则是git 1.8.2中的新规则.如果您的git比那个旧,git也使用标签的分支规则,允许快速转发而不"强制更新".
6但没有not-for-merge
这个时间.基本上,当你提供无冒号的refspecs时,git fetch
假定它们是"for merge"并将它们放入FETCH_HEAD
以便git merge FETCH_HEAD
找到它们.(我没有测试过非分支引用会发生什么.)