【问题标题】:Is there any method to efficiently apply large git patches?有什么方法可以有效地应用大型 git 补丁吗?
【发布时间】:2022-11-06 18:25:31
【问题描述】:

我们收到了一个修改了大约 17000 个文件的大补丁。它的大小是5.2G。使用git apply -3 应用补丁时,12 小时后仍未完成。

我们将补丁拆分为每个文件的较小补丁,并一个一个地应用它们,这样至少我们可以看到进度。

再一次,它卡在了其中一个文件补丁上,仍然有 111M 大。它修改 HTML 文件。

我们将这个文件补丁分成每个块的更小的补丁,得到大约 57000 个块补丁。每个块补丁大约需要 2-3 秒,因此比应用文件补丁需要更多时间。我会尝试将它分成更多的块。

有什么方法可以有效地应用这么大的补丁吗?谢谢。

更新:

正如@ti7 建议的那样,我尝试了patch,它解决了这个问题。

就我而言,我们有两种大补丁。

一种是添加/删除大型二进制文件,二进制文件的内容作为文本包含在补丁中。其中一个二进制文件为 188M,删除它的补丁大小为 374M。

另一种是修改大文本,有数百万的删除和插入。其中一个文本文件是 70M 之前和 162M 之后。补丁大小为 181M,有 2388623 个插入和 426959 个删除。

经过一些测试,我认为这里的“大”描述了插入和删除的数量。

对于二进制补丁,

  • git apply -3, 7 秒
  • git 应用,6 秒
  • 补丁,5 秒

对于文本补丁,

  • git apply -3,卡住,10 分钟后未完成
  • git apply,卡住,10 分钟后未完成
  • 补丁,3 秒

二进制文件只有 1 次插入和/或 1 次删除。 git applypatch 可以在几秒钟内完成。都是可以接受的。

文本有太多的插入和删除。显然,patch 在这种情况下要好得多。我在patch 上阅读了一些帖子,并了解到patch 的某些版本无法用于添加/删除/重命名文件。幸运的是,我机器上的patch 运行良好。

因此,我们将一体化补丁拆分为每个文件的较小补丁。我们首先尝试timeout 10s git apply -3 file_patch。如果无法在 10 秒内完成,请尝试 timeout 10s patch -p1 < file_patch

最后,用了大约 1 个半小时的时间,将 17000 个补丁全部打上。这比应用多合一补丁并卡住 12 小时而无所事事要好得多。

我也试过patch -p1 < all_in_one_patch。只用了1m27s。所以我认为我们可以进一步改进我们的补丁流程。

【问题讨论】:

  • 我从未使用过大于数十兆字节的 git 存储库。我的意思是整个回购,包括从项目开始的所有历史。我什至无法想象 5.2 GB 的变更集。有人提交了一些大型二进制文件吗?
  • @Code-Apprentice 就我而言,大型二进制文件不是问题。一个二进制文件只有一个块。它会很快失败或成功。问题是补丁文件太多,一些文本文件有太多块。
  • 您也许可以使用patch 而不是git apply,然后添加并提交
  • 作为背景知识,git apply 在开始写出修改后的文件之前尝试将整个补丁应用到内存中。目的是在补丁中途失败的情况下,它不会留下部分修改的工作树。
  • @ti7 我试过patch。它非常迅速地应用了 111M 文件补丁,仅需 2 秒。请您将其写为答案,以便我接受吗?

标签: git git-apply


【解决方案1】:

您也许可以使用patch (Wikipedia) 而不是git apply 来加快修补速度!

据我所知,patch 直接逐行输出一个新文件,在更改时拼接,而git apply 进行额外的上下文检查(正如@j6t notes in a comment,虽然我还没有确认,但会尝试在写出之前立即加载和修补整个文件)

【讨论】:

  • 谢谢!我用cd path_to_repository; patch -p1 < path_to_patch
【解决方案2】:

patch 的另一个论点:git apply 现在官方限制为 1GB。

使用 Git 2.39(2022 年第四季度),“git apply(man)将其输入限制为略小于 1 GiB。

commit f1c0e39(2022 年 10 月 25 日)Taylor Blau (ttaylorr)
(由Taylor Blau -- ttaylorr --commit c41ec63 中合并,2022 年 10 月 30 日)

apply:拒绝大于 ~1 GiB 的补丁

报告人:정재우
推荐人:约翰内斯辛德林
签字人:Taylor Blau

应用代码不准备处理非常大的文件。
它在某些地方使用“int”,在其他地方使用“unsigned long”。

当在两种类型之间切换时,这种组合会导致不幸的问题。
使用“int”会阻止我们处理大文件,因为大偏移量会环绕并溢出到小的负值,这可能会导致错误行为(例如访问带有负偏移量的补丁缓冲区)。

即使在“long”与“int”大小相同的 LLP64 平台上,从“unsigned long”转换为“int”也存在截断问题,因为前者是无符号的,而后者不是。

避免git apply 中潜在的溢出和截断问题(man),应用与dcd1742 中类似的处理(“xdiff:拒绝大于~1GB 的文件”,2015-09-24,Git v2.7.0-rc0 -- mergebatch #2 中列出),其中@ 987654348@ 代码被教导以类似的原因拒绝大文件。

最大大小的选择有些随意,但是选择一个接近 1 GB 的值可以让我们将其加倍而不会溢出 2^31-1(之后我们的值将环绕为负数)。
为了给自己一点额外的余量,最大的补丁大小是比完整的 GiB 小一个 MiB,这给了我们一些麻烦,以防我们分配“(records + 1) * sizeof(int)”或类似的东西。

幸运的是,这些转换问题的安全影响相对来说是无趣的,因为需要说服受害者应用恶意补丁。

【讨论】:

    猜你喜欢
    • 2013-04-17
    • 2013-06-25
    • 2023-03-09
    • 1970-01-01
    • 2011-07-06
    • 2015-04-29
    • 2011-12-09
    • 1970-01-01
    • 2019-12-19
    相关资源
    最近更新 更多