41

我在共享主机上托管一个 git repo。我的 repo 中必然有几个非常大的文件,现在每次我尝试在 repo 上运行“git gc”时,我的进程都会因使用过多内存而被共享主机提供商杀死。有没有办法限制 git gc 可以消耗的内存量?我希望它可以用内存来换取速度,并且只需要更长的时间来完成它的工作。

4

5 回答 5

42

我使用了此链接中的说明。与Charles Baileys建议的想法相同。

命令的副本在这里:

git config --global pack.windowMemory "100m"
git config --global pack.packSizeLimit "100m"
git config --global pack.threads "1"

这对我在具有共享主机帐户的 hostgator 上有用。

于 2012-01-06T16:59:42.417 回答
16

是的,请查看帮助页面git config并查看pack.*选项,特别是pack.depth、和。pack.windowpack.windowMemorypack.deltaCacheSize

这不是一个完全精确的大小,因为 git 需要将每个对象映射到内存中,因此无论窗口和增量缓存设置如何,一个非常大的对象都会导致大量内存使用。

您可能会更幸运地在本地打包并将打包文件“手动”传输到远程端,添加.keep文件以便远程 git 不会尝试完全重新打包所有内容。

于 2010-06-22T18:05:04.700 回答
15

Git repack 的内存使用是:(pack.deltaCacheSize + pack.windowMemory) × pack.threads. 各自的默认值为 256MiB、无限制、nproc。

增量缓存没有用:大部分时间都花在计算滑动窗口上的增量上,其中大部分都被丢弃了;缓存幸存者以便它们可以被重用一次(在编写时)不会改善运行时间。该缓存也不在线程之间共享。

默认情况下,窗口内存通过pack.window( gc.aggressiveWindow) 限制。以这种方式限制打包是一个坏主意,因为工作集的大小和效率会有很大差异。最好将两者都提高到更高的值并依靠pack.windowMemory限制窗口大小。

最后,线程具有分裂工作集的缺点。降低pack.threads和增加pack.windowMemory以使总数保持不变应该可以改善运行时间。

repack 有其他有用的可调参数(pack.depth, pack.compression,位图选项),但它们不会影响内存使用。

于 2015-01-08T11:50:30.137 回答
7

您可以使用关闭 delta 属性来仅为这些路径名的 blob 禁用 delta 压缩:

foo/.git/info/attributes(或者foo.git/info/attributes如果它是一个裸存储库)(请参阅 gitattributes 中的 delta 条目查看gitignore以了解模式语法):

/large_file_dir/* -delta
*.psd -delta
/data/*.iso -delta
/some/big/file -delta
another/file/that/is/large -delta

这不会影响存储库的克隆。要影响其他存储库(即克隆),请将属性放在.gitattributes文件中而不是(或附加于)info/attributes文件中。

于 2010-06-22T20:00:05.143 回答
3

Git 2.18 (Q2 2018) 将改善 gc 内存消耗。
在 2.18 之前,“ git pack-objects”在工作时需要分配大量的“ struct object_entry”:缩小它的大小对性能有很大帮助
这影响git gc

See commit f6a5576 , commit 3b13a5f , commit 0aca34e , commit ac77d0c , commit 27a7d06 , commit 660b373 , commit 0cb3c14 , commit 898eba5 , commit 43fa44f , commit 06af3bb , commit b5c0cbd , commit 0c6804a , commit fd9b1ba , commit 8d6ccce , commit 4c2db93 (14 Apr 2018)作者Nguyễn Thái Ngọc Duy ( pclouds)
(由Junio C Hamano 合并 -- gitster--提交 ad635e8, 2018 年 5 月 23 日)

pack-objects: 重新排序成员以缩小struct object_entry

以前的补丁在这个结构中留下了很多洞和填充。
此补丁对成员重新排序并将结构缩小到 80 字节(从 64 位系统上的 136 字节,在完成任何字段缩小之前),有 16 位备用(当我们真的用完位时,在 in_pack_header_size 中还有更多) .

这是一系列内存减少补丁中的最后一个(第一个参见“ pack-objects: a bit of document about struct object_entry ”)。

总体而言,他们将重新打包的内存大小linux-2.6.git从 3.747G 减少到 3.424G,或减少了大约 320M,减少了 8.5%。
在本系列中,重新打包的运行时间保持不变。
Ævar 对他可以访问(大于linux-2.6.git)的大型 monorepo 进行的测试显示减少了 7.9%,因此总体预期改进应该在 8% 左右。


使用 Git 2.20(2018 年第四季度),将更容易检查存在于一个分叉中的对象是否未针对未出现在同一分叉存储库中的另一个对象进行增量。

请参阅Christian Couder ( ) 的提交 fe0ac2f提交 108f530提交 f64ba53(2018 年 8 月 16 日。 帮助者:Jeff King ( )Duy Nguyen ( )。 请参阅Jeff King ( )的提交 9eb0986提交 16d75fa提交 28b8a73提交 c8d521f(2018 年 8 月 16 日) 。 帮助者:Jeff King ( )Duy Nguyen ( )(由Junio C Hamano 合并 -- --提交 f3504eachriscool
peffpclouds
peff
peffpclouds
gitster, 2018 年 9 月 17 日)

pack-objects: 将 ' layer' 移入 ' struct packing_data'

这将“struct object_entry”的大小从 88 个字节减少到 80 个字节,从而使打包对象更加高效。

例如,在具有 12M 对象的 Linux存储库上,git pack-objects --all即使未使用层功能,也需要额外的 96MB 内存。


请注意,Git 2.21(2019 年 2 月)修复了一个小错误:“ git pack-objects”错误地使用了未初始化的互斥锁,该问题已得到纠正。

请参阅提交 edb673c提交 459307b(2019 年 1 月 25 日)由Patrick Hogg (``)
帮助者:Junio C Hamano ( gitster)
(由Junio C Hamano 合并 -- gitster--d243a32 提交中,2019 年 2 月 5 日)

pack-objects: 将读取互斥体移动到packing_data结构体

ac77d0c (" pack-objects:shrink size field in struct ", 2018-04-14) 在新引入的线程安全并行调用中 object_entry增加了 read_lock/read_unlock 的额外用法。 不幸的是也用于串行代码,其中一些在第一次调用 . 因此,不能保证读取互斥锁被初始化。oe_get_size_slowtry_delta()
oe_get_size_slowll_find_deltas

通过将读取互斥体移动到packing_data并在 prepare_packing_data 中初始化它来解决这个问题,该数据在cmd_pack_objects.


Git 2.21(2019 年 2 月)仍然找到了另一种缩小包大小的方法,“ git pack-objects”学习了另一种算法来计算要发送的对象集,该算法将生成的包文件换掉以节省遍历成本以支持小推送。

pack-objects: 创建pack.useSparse设置

' ' 中的 ' --sparse' 标志git pack-objects将用于枚举对象的算法更改为对于推送仅更改工作目录的一小部分的新对象的单个用户而言更快的算法。
不建议将稀疏算法用于服务器,因为它可能会发送出现在整个工作目录中的新对象。

创建pack.useSparse启用此新算法的“”设置。
这允许 ' git push' 使用此算法,而无需--sparse通过四个级别的run_command() 调用一直传递 ' ' 标志。

如果设置了“ --no-sparse”标志,则覆盖此配置设置。

配置包文档现在包括:

pack.useSparse:

如果为 true,当存在 ' ' 选项时,Git 将默认使用 ' --sparse' 中git pack-objects的 ' --revs' 选项。
该算法仅遍历出现在引入新对象的路径中的树。

当计算一个包以发送一个小变化时,这可以带来显着的性能优势。

但是,如果包含的提交包含某些类型的直接重命名,则可能会将额外的对象添加到包文件中。

有关具体说明,请参见“git push对于一个巨大的 repo 来说非常慢”。


注意:正如 Git 2.24 中所评论的,类似的设置pack.useSparse仍然是实验性的。

请参阅Derrick Stolee ( ) 的提交 aaf633c提交 c6cc4c5提交 ad0fb65提交 31b1de6提交 b068d9a提交 7211b9e(2019 年 8 月 13 日(由Junio C Hamano 合并 -- --提交 f4f8dfe中,2019 年 9 月 9 日)derrickstolee
gitster

repo-settings: 创建feature.experimental设置

' feature.experimental' 设置包括未提交成为默认值的配置选项,但可以使用额外的测试。

更新以下配置设置以采用新的默认值,repo_settings如果尚未使用结构,则使用它们:

  • 'pack.useSparse=true'
  • 'fetch.negotiationAlgorithm=skipping'

在 Git 2.26(2020 年第一季度)中,“ git pack-objects”重用存储在现有包中的对象以生成其结果的方式得到了改进。

请参阅提交 d2ea031提交 92fb0db提交 bb514de提交 ff48302提交 e704fc7提交 2f4af77提交 8ebf529提交 59b2829提交 40d18ff提交 14fbd26(2019 年 12 月 18 日)和提交 56d913cbe提交 56d9cbe ,2019 年 9 月bab28 ( peff)
(由Junio C Hamano 合并 -- gitster--提交 a14aebe中,2020 年 2 月 14 日)

pack-objects: 改进部分包文件重用

帮助人:Jonathan Tan
签字人:Jeff King
签字人:Christian Couder

从现有包文件中重用增量的旧代码只是试图逐字转储整个包段。这比将对象实际添加到装箱单中的传统方式要快,但它并不经常启动。这个新代码实际上是在寻求一个中间立场:做一些针对每个对象的工作,但比我们传统上做的要少。

新代码的一般策略是从我们将包含的包文件中制作对象的位图,然后对其进行迭代,将每个对象完全按照我们的磁盘包中的内容写出,但将其添加到我们的包列表中(这会消耗内存,并增加增量的搜索空间)。

一个复杂的问题是,如果我们省略了一些对象,我们就不能针对我们没有发送的基础设置增量。所以我们必须检查每个对象try_partial_reuse()以确保我们有它的增量。

关于性能,在最坏的情况下,我们可能会交错发送或不发送的对象,并且我们将拥有与对象一样多的块。但在实践中,我们发送大块。

例如,在 GitHub 服务器上打包 torvalds/linux 现在重用了 650 万个对象,但只需要大约 50k 块。


使用 Git 2.34(2021 年第四季度),git repack它本身(由 使用git gc)受益于内存使用量的减少。

请参阅Taylor Blau ( ) 的提交 b017334提交 a9fd2f2提交 a241878(2021 年 8 月 29 日(由Junio C Hamano 合并——提交 9559de3中,2021 年 9 月 10 日)ttaylorr
gitster

builtin/pack-objects.c:删除重复的哈希查找

签字人:Taylor Blau

在来自08cdfb1 ("pack-objects --keep-unreachable", 2007-09-16, Git v1.5.4-rc0 -- merge ) 的原始代码中,我们将每个对象添加到类型为 ``obj->type ,where objfrom的装箱单中lookup_unknown_object()
除非我们已经查找并解析了对象,否则它将是OBJ_NONE.
没关系,因为oe_set_type()type_valid位设置为“0”,我们稍后确定实际类型。

所以我们从对象查找中唯一需要的是访问该flags字段,以便我们可以标记我们已经添加了对象OBJECT_ADDED以避免再次添加它(我们可以直接传递OBJ_NONE而不是从对象中获取它)。

add_object_entry()已经拒绝重复!这是自7a979d9以来的行为(“Thin pack - create packfile with missing delta base.”,2006-02-19,Git v1.3.0-rc1 -- merge),但08cdfb1没有利用它。
此外,要进行OBJECT_ADDED检查,我们必须在obj_hash.

所以我们可以lookup_unknown_object()完全挂断电话,也可以OBJECT_ADDED挂断标志,因为我们在这里触摸的位置是唯一检查它的位置。

最后,我们执行相同数量的哈希查找,但额外的好处是我们不会浪费内存分配OBJ_NONE对象(如果我们正在遍历,我们最终会需要它,但这段代码路径的重点是不要穿越)。


Git 2.36(2022 年第二季度)更正了配置变量fetch.negotiationAlgorithm和配置变量之间的交互。feature.experimental

请参阅Elijah Newren ( ) 的commit 714edc6commit a9a136ccommit a68c5b9(2022 年 2 月 2 日(由Junio C Hamano 合并 -- --70ff41f 提交中,2022 年 2 月 16 日)newren
gitster

repo-settings:重命名传统的默认值fetch.negotiationAlgorithm

签字人:以利亚·纽伦

将传统默认fetch.negotiationAlgorithm名称命名为“ consecutive”。
还允许选择 ' default' 让 Git 在选择之间做出决定(目前,skipping如果feature.experimental为真则选择 ' ' consecutive,否则选择 ' ')。
相应地更新文档。

git config现在在其手册页中包含:

在协商要由服务器发送的包文件的内容时,控制如何发送有关本地存储库中提交的信息。

  • 设置为“ consecutive”以使用遍历连续提交检查每个提交的算法。
  • 设置为“ skipping”以使用跳过提交以加快收敛速度​​的算法,但可能会导致包文件过大;或设置为 " noop" 根本不发送任何信息,这几乎肯定会导致包文件大于必要,但会跳过协商步骤。
  • 设置为“默认”以覆盖之前所做的设置并使用默认行为。

默认通常为“ consecutive”,但如果feature.experimental为真,则默认为“ skipping”。
未知值将导致 ' git fetch' 出错 ( unknown fetch negotiation algorithm)。

于 2018-05-25T23:11:03.820 回答