【问题标题】:Preventing git push from sending entire repo if not up-to-date如果不是最新的,防止 git push 发送整个 repo
【发布时间】:2011-05-05 09:25:51
【问题描述】:

相关问题:why does Git send whole repository each time push origin master

简短版:在使用两个 Git 存储库时,即使 99% 的提交对象相同,使用 git push 将提交发送到存储库Borigin 设置为指向repo A 会导致所有对象(200MB +)被传输。

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

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

这非常有效,尤其是在推送到 CI 存储库之前执行 git fetch; git rebase origin/master 时; Git 只发送不在origin/master 中的提交对象。如果在推送之前跳过 fetch/rebase 步骤,该过程仍然有效,但 Git 似乎将大部分提交对象(如果不是全部)发送到 CI 存储库——目前价值超过 200MB。 (我们的 repo 的新克隆为 225MB。)

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

我们的存储库由 Gitosis 0.2 提供服务,我们的客户绝大多数都在运行 msysgit 1.7.3.1-preview。

【问题讨论】:

    标签: git continuous-integration msysgit gitosis git-push


    【解决方案1】:

    ...自动变基为 master...

    我认为这就是问题的根源。每次你的 CI 服务器执行这个 auto-rebase 步骤时,它都会创建一个完整的 new 提交集合,这些提交相对于当前分支和 master 分支最近的共同祖先。

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

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

    【讨论】:

    • 是的,当 CI 服务器上发生 rebase 时,提交 SHA 会改变,但是,这应该不会导致 整个存储库被重新-发送;使用 Git 的规则之一是“不要重写已经在 master 中的提交”——我们没有违反这条规则。最多,我预计会发送到共同祖先的提交。
    • 此外,我们已经检查了两个存储库上的 SHA 以了解我们的提交,并且它们确实匹配。
    • 您可以尝试的一种方法是使用git push --dry-run --verbose 并查看您是否可以确定正在发送哪些对象。
    • 使用--dry-run --verbose 运行不会提供有关将发送哪些对象的任何信息,只是推送会创建一个新的远程分支。
    【解决方案2】:

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

    $ git fetch origin master
    $ git push user@host:repo.git HEAD:refs/heads/commit128952690069
    

    在我们的例子中,获取特定分支到FETCH_HEAD 很重要;这样,用户的本地分支状态将不受影响,但我们仍会从主存储库收到最新的对象集;当 Git 开始打包对象时,以下git push 将始终存在祖先提交。

    我用git pack-objects 做了一些工具:如果构建一个包含提交<common_ancestor>..HEAD 的包文件,它只会打包所需的数据:

    $ echo $(git merge-base master origin/master)..HEAD | git pack-objects --revs --thin --stdout --all-progress-implied > packfile
    

    但是,在存储库处于相同状态时发出 git push 会导致 所有 对象被打包并发送。

    我怀疑发生的情况是,在连接到 Git 存储库时,会收到存储库中最新修订的 SHA——如果 Git 在本地没有该 SHA 表示的提交对象,它就无法运行 git merge-base 来确定共同祖先;因此,它必须将所有对象发送到远程仓库。如果该提交对象确实存在,则git merge-base 成功,并且可以参考共同祖先来构建包文件。

    【讨论】:

    • 这对我也有用,但我仍然觉得它很神秘。
    【解决方案3】:

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

    git克隆大师 (... 做工作 ...) git push ci 分支 (...... CI 做了一个 rebase,改变了你推送的所有提交哈希..) (...... CI 进行测试并推动掌握......) (... 现在 master 和 CI 匹配但是你刚刚推送的所有提交的哈希值 除了您的本地机器外不存在任何地方...) (... 做工作 ...) git push ci 分支

    最后一次推送将包含您第一次推送的整个提交集,因为 CI 的变基更改了它们的所有哈希值,而您仍在处理您创建的原始提交。

    【讨论】:

    • 但是他们不会有一个完全不同的哈希集,对吧?本地、主和 CI 存储库应该有一个共同的祖先提交,在所有机器上具有相同的 SHA; Git 需要它,否则合并/变基会分崩离析。有没有办法向 Git 暗示那个祖先可能是什么?它似乎只是错误地选择了它。
    • 你说得对,当我完全说我已经编辑了我的答案以澄清时,我并不清楚。变基会将所有内容更改回共同的祖先。您正在谈论的工作流程的问题是,如果每次您推送到 CI 时都会发生这种情况(如果其他人也在推送,这似乎不太可能)那么那个共同的祖先很可能就是那个提交首次克隆存储库时的当前值。
    • 测试这是否是问题的一种方法是再次克隆 master 并查看来自那里的推送是否会做同样的事情。如果这是正在发生的事情,那么来自新克隆的推送将不会这样做。一个新的克隆不会立即出现问题,因为历史上的差异不是那么大。除非你改变 CI 的作用,否则它会随着时间的推移变得越来越糟。
    猜你喜欢
    • 2022-09-23
    • 1970-01-01
    • 2013-08-15
    • 2015-03-19
    • 2020-11-11
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2019-11-28
    相关资源
    最近更新 更多