防止git push发送整个回购协议(如果不是最新的)

防止git push发送整个回购协议(如果不是最新的),git,continuous-integration,msysgit,gitosis,git-push,Git,Continuous Integration,Msysgit,Gitosis,Git Push,相关问题: 简短版本:当使用两个Git存储库时,即使99%的提交对象是相同的,当origin设置为指向repo a时,使用Git push向存储库B发送提交会导致传输所有对象(200MB+) 更长的版本:我们在持续集成服务器上设置了第二个Git存储库。在本地准备好提交对象之后,我们不再像通常那样直接推送到origin/master,而是将更改推送到第二个存储库上的分支。CI服务器拾取新分支,自动将其重定到master,运行我们的集成测试,如果一切正常,将分支推到master repo上的orig

相关问题:

简短版本:当使用两个Git存储库时,即使99%的提交对象是相同的,当
origin
设置为指向repo a时,使用
Git push
向存储库B发送提交会导致传输所有对象(200MB+)

更长的版本:我们在持续集成服务器上设置了第二个Git存储库。在本地准备好提交对象之后,我们不再像通常那样直接推送到
origin/master
,而是将更改推送到第二个存储库上的分支。CI服务器拾取新分支,自动将其重定到
master
,运行我们的集成测试,如果一切正常,将分支推到master repo上的
origin/master

CI服务器还定期调用
git fetch
,从主repo中检索
origin/master
的最新副本,以防有人绕过CI流程并直接推送

这非常有效,尤其是在执行
git获取时;在推送到CI回购之前,git重新基准原始/主
;Git只发送不在
origin/master
中的提交对象。如果在推送之前跳过了获取/重基步骤,该过程仍然有效,但Git似乎会将大部分提交对象(如果不是全部的话)发送到CI repo——目前价值超过200MB。(我们回购协议的最新克隆时钟频率为225MB。)

我们做错什么了吗?有没有办法纠正这种行为,使Git只发送它在CI repo上形成分支所需的提交对象?显然,我们可以通过预推
git获取来解决这个问题;git rebase origin/master
,但感觉我们应该能够跳过这一步,特别是因为直接推送到master repo不会出现同样的问题

我们的回购协议由Gitosis 0.2提供,我们的客户绝大多数运行msysgit 1.7.3.1-preview

…自动将其重设为
master

我认为这就是问题的根源所在。每次您的CI服务器执行此自动重基步骤时,它都会创建一组相对于当前分支和主分支最近的公共祖先的全新提交

下次您将代码推送到CI服务器时,它实际上不再具有所有这些对象(任何活动头都无法访问这些对象),因此它会请求您的客户端再次发送所有对象


通过查看正在进行的提交的SHA1提交ID,您应该能够看到这种情况的发生。您可能会发现本地提交的提交ID不再与CI服务器上重新设置基础的分支中的相应提交ID匹配。

听起来您的本地存储库与CI服务器存储库不同步,从您推送到CI服务器这一事实意味着您的本地存储库有一组不同的提交哈希。它可以是这样的:

git clone master (... do work ...) git push ci branch (... CI does a rebase that changes all the commits hashes you pushed ..) (... CI does its' testing and pushes to master ...) (... Now master and CI match but the hashes of all the commits you just pushed don't exist anywhere except your local machine ...) (... do work ...) git push ci branch git克隆主机 (…做工作…) git-push-ci分支 (…CI执行重基,以更改您推送的所有提交哈希…) (…CI进行“测试并推送至主机…” (…现在master和CI匹配,但是您刚才推送的所有提交的哈希都匹配 除了您的本地机器之外,任何地方都不存在…) (…做工作…) git-push-ci分支
最后一次推送将包含来自第一次推送的整个提交集,因为CI的rebase更改了它们的所有哈希,而您仍在处理您创建的原始提交

事实证明,解决这个问题的最简单方法是在推送之前提取:

$ git fetch origin master
$ git push user@host:repo.git HEAD:refs/heads/commit128952690069
在我们的例子中,重要的是将特定分支提取到
fetch_HEAD
;这样,用户的本地分支状态将不受影响,但我们仍然从主存储库接收最新的对象集;当git开始打包对象时,下面的
git push
将始终存在祖先提交

我对git pack对象做了一些处理:如果构建一个包含提交
.HEAD的包文件,它只打包所需的数据:

$ echo $(git merge-base master origin/master)..HEAD | git pack-objects --revs --thin --stdout --all-progress-implied > packfile
但是,在存储库处于相同状态的情况下发出
git push
,会导致打包和发送所有对象


我猜想在连接到Git repo时,会收到repo中最新版本的SHA——如果Git在本地没有由该SHA表示的提交对象,它就无法运行
Git merge base
来确定公共祖先;因此,它必须将所有对象发送到远程repo。如果该提交对象确实存在,则
git merge base
成功,并且可以参照公共祖先构建包文件。

是的,当CI服务器上发生重基时,提交SHA将更改,但这不应导致重新发送整个存储库;使用Git的规则之一是“不要重写已经在
master
中的提交”——我们没有违反这个规则。最多,我会期望将提交发送到一个共同的祖先。此外,我们已经检查了两个存储库中的SHA以进行提交,您可以尝试的一种方法是使用
git-push--dry-run--verbose
,看看是否可以确定要发送哪些对象。使用
--dry-run--verbose
运行不会给出要发送哪些对象的任何信息,只是推送会创建一个新的远程分支。但它们不会有完全不同的哈希集,对吗?本地、主和CI repo应该有一个在所有机器上都具有相同SHA的共同祖先提交;Git要求这样做,否则合并/重新定基将分崩离析。有没有办法向Git提示该祖先可能是什么?它似乎只是在选择