Git推送孤立分支重复上传相同文件的解决方案问询
Git推送独立分支时重复上传相同对象的问题与解决办法
问题场景复现
你大概率遇到过这种头疼的情况:
- 创建新分支,提交一个10MB文件并完成推送,流程正常
- 接着创建孤立分支(无父提交的空分支),提交同一个完全未修改的10MB文件(SHA哈希完全一致),推送时却发现Git又重新上传了一遍这个大文件
可以通过以下命令复现这个问题:
$ mkdir git-test && cd git-test $ git init $ git remote add origin git@gitlab.com:username/projectname.git # 创建10MB测试文件 $ head -c 10000000 /dev/urandom > dummy $ git add dummy $ git commit -m 'init' # 首次推送,上传全部内容,符合预期 $ git push origin master Counting objects: 3, done. Delta compression using up to 6 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 9.54 MiB | 1.13 MiB/s, done. Total 3 (delta 0), reused 0 (delta 0) # 创建不基于master的空孤立分支 $ git checkout --orphan branch2 # 再次添加相同文件 $ git add dummy $ git commit -m 'init on branch2' # 服务器已存在该对象,但仍重新上传10MB文件 $ git push origin branch2 Counting objects: 3, done. Delta compression using up to 6 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 9.54 MiB | 838.00 KiB/s, done.
预期vs实际差异
按Git的设计逻辑,靠SHA识别文件对象,应该能检测到服务器上已经存在这个文件,从而避免重复上传;但实际情况是,只要分支完全独立(无共同历史),推送时所有关联对象都会被重新上传——哪怕是大量重复的小文件,在低带宽环境下特别影响效率,比如机器学习实验场景:每次创建无父提交的新提交并关联自定义引用(如refs/jobs/my-experiment-name),大量重复文件反复上传会严重拖慢工作节奏。
问题根源
这是Git本身的协议设计导致的:Git的pack同步协议是基于提交历史来判断需要传输哪些对象的。两个独立分支没有共同父提交,Git不会单独去校验服务器上是否存在某个孤立的对象,只会检查当前提交链里的对象在服务器的提交历史中是否存在,所以哪怕文件对象完全一致,也会被重复传输。
解决办法
我们可以用自定义逻辑绕过这个限制,核心思路是先让Git明确服务器上已有的对象,只推送缺失的部分:
- 提取目标提交关联的所有对象SHA(排除父提交相关的对象)
- 将SHA列表发送到服务器,获取服务器确实缺少的对象列表
- 仅基于这些缺失对象构建pack文件推送,最后更新对应的远程引用
你可以自己实现这个逻辑,也可以用现成的自定义脚本方案,使用后的效果非常明显:
marc@osx ~/git-test (branch11*) $ # 按前述方式创建branch11 marc@osx ~/git-test (branch11*) $ python git-sync.py refs/heads/branch11 Counting objects: 1, done. Writing objects: 100% (1/1), 158 bytes | 158.00 KiB/s, done. Total 1 (delta 0), reused 0 (delta 0) marc@osx ~/git-test (branch11*) $ git push origin branch11 Everything up-to-date
可以看到,这次只同步了提交对象本身,重复的大文件和树对象都没有被重复上传。
内容的提问来源于stack exchange,提问作者MMM




