无本地分支时Git fetch拉取提交哈希行为异常原因咨询
Git哈希拉取重复拉取行为的原因:设计如此还是技术限制?
问题描述
当本地没有分支时,通过提交哈希执行git fetch会重新拉取所有祖先提交(即使这些祖先已存在于本地Git数据库中);但当有本地分支指向某一祖先提交时,Git不会重复拉取。请问这是技术限制还是设计如此?使用版本为Git for Windows 2.42.0。
复现步骤
- 准备两台计算机A(IP:10.0.0.1)和B(IP:10.0.0.2)。
- 在B的代码根目录启动git daemon:
git daemon --export-all --enable=upload-archive --base-path=.
- 在A初始化仓库并通过提交哈希从B拉取:
git init git fetch --no-tags --progress --prune -- git://10.0.0.2/ d3571ce95af4dc281f142add33384abc5e574671 git checkout FETCH_HEAD
- 在B基于上述提交(d3571c)创建空提交15aab0fd8b937eb3bb01841693f35dcb75da2faf:
git commit --allow-empty -m "An empty commit"
- 在A通过哈希拉取该空提交:
git fetch --no-tags --progress --prune -- git://10.0.0.2/ 15aab0fd8b937eb3bb01841693f35dcb75da2faf
- 此时会发现Git尝试重新拉取所有对象:
remote: Enumerating objects: 49535, done. remote: Counting objects: ... remote: Compressing objects: ... Receiving objects: ... ...
- 但当A创建本地分支后,情况完全不同:
# 在B创建另一个空提交7c6e5dd1bacb3b48fdffba2ed096097eb172497d git commit --amend -m "Another empty commit" # 在A创建本地分支后拉取新空提交 git branch whatever git fetch --no-tags --progress --prune -- git://10.0.0.2/ 7c6e5dd1bacb3b48fdffba2ed096097eb172497d
此时输出为:
remote: Enumerating objects: 1, done. ...
解答
这是设计如此,核心原因在于Git的协商传输机制:
Git执行fetch时,会和远程服务器做对象协商——本地告知服务器自己已拥有的对象,服务器仅发送本地缺失的部分。但协商过程中,默认只有被引用的对象(比如分支、标签、远程跟踪分支)会被纳入本地“已拥有对象”列表。
当本地只有游离提交(比如通过git checkout FETCH_HEAD得到的状态,无本地分支指向)时,这些提交不属于任何引用,Git不会将其加入协商的“have”列表。服务器不知道你已拥有这些祖先提交,因此会发送目标哈希回溯的所有对象。
而创建本地分支后,分支作为引用指向了某个祖先提交,Git会把该分支对应的提交及其所有可达祖先都纳入协商的“have”列表。服务器收到信息后,就知道只需发送目标提交本身(这里是空提交,仅1个对象),无需重复发送已存在的祖先对象。
你也可以手动指定--negotiation-tip参数,让Git把游离提交纳入协商范围,比如:
git fetch --no-tags --progress --prune --negotiation-tip=d3571ce95af4dc281f142add33384abc5e574671 git://10.0.0.2/ 15aab0fd8b937eb3bb01841693f35dcb75da2faf
这样即使没有本地分支,也不会重复拉取祖先对象。
内容的提问来源于stack exchange,提问作者lxvs
相关产品推荐
相关产品推荐

