我在共享主机上托管一个 git repo。我的 repo 中必然有几个非常大的文件,现在每次我尝试在 repo 上运行“git gc”时,我的进程都会因使用过多内存而被共享主机提供商杀死。有没有办法限制 git gc 可以消耗的内存量?我希望它可以用内存来换取速度,并且只需要更长的时间来完成它的工作。
5 回答
我使用了此链接中的说明。与Charles Baileys建议的想法相同。
命令的副本在这里:
git config --global pack.windowMemory "100m"
git config --global pack.packSizeLimit "100m"
git config --global pack.threads "1"
这对我在具有共享主机帐户的 hostgator 上有用。
是的,请查看帮助页面git config
并查看pack.*
选项,特别是pack.depth
、和。pack.window
pack.windowMemory
pack.deltaCacheSize
这不是一个完全精确的大小,因为 git 需要将每个对象映射到内存中,因此无论窗口和增量缓存设置如何,一个非常大的对象都会导致大量内存使用。
您可能会更幸运地在本地打包并将打包文件“手动”传输到远程端,添加.keep
文件以便远程 git 不会尝试完全重新打包所有内容。
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
,位图选项),但它们不会影响内存使用。
您可以使用关闭 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
文件中。
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
peff
pclouds
peff
peff
pclouds
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_slow
try_delta()
oe_get_size_slow
ll_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,
whereobj
from的装箱单中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 714edc6、commit a9a136c、commit 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
)。