Git GC ——侵略性 VS Git 重装

我正在寻找减小 git存储库大小的方法。大多数情况下,搜索会让我找到 git gc --aggressive。我也读到过,这不是首选的方法。

为什么? 如果我运行 gc --aggressive,我应该注意什么?

建议 git repack -a -d --depth=250 --window=250超过 gc --aggressive。为什么?repack如何减小存储库的大小?此外,我不是很清楚的标志 --depth--window

我应该在 gcrepack之间选择什么? 我应该什么时候使用 gcrepack

56064 次浏览

什么时候应该使用 gc & repack?

正如我在“ Git 垃圾收集似乎不能完全工作”中提到的,git gc --aggressive本身既不充分,甚至也不足够。
而且,作为 我在下面解释,通常不需要。

最有效的组合将是增加 git repack,但也增加 git prune:

git gc
git repack -Ad      # kills in-pack garbage
git prune           # kills loose garbage

Note: Git 2.11 (Q4 2016) will set the default gc aggressive depth to 50

第七季,第7集(2016年8月11日) by 杰夫 · 金(peff)
(由 Junio C Hamano -- gitster --于2016年9月21日在 提交0952ca8合并)

gc: 默认侵入深度为50

git gc --aggressive”用来限制三角链长度为250, which is way too deep for gaining additional space savings and is 不利于运行时性能。
限额已降至50。

总结是: 当前默认的250不能 节省了大量的空间,并且浪费了 CPU。这不是一个好的权衡。

git-gc的“ --aggressive”标志有三个作用:

  1. 使用“ -f”抛弃现有的增量,从头开始重新计算
  2. 使用“—— window = 250”查找 delta
  3. use "--depth=250" to make longer delta chains

项目(1)和(2)是一个“积极的”重新包装很好的匹配。
他们要求重新包做更多的计算工作,希望得到一个更好的包。你在重新包装过程中支付成本,而其他操作只看到收益。

Item (3) is not so clear.
允许更长的链意味着对 delta 的限制更少,这意味着可能会找到更好的并节省一些空间。
但这也意味着访问 delta 的操作必须遵循更长的链,这会影响它们的性能。
因此,这是一种权衡,而且目前尚不清楚这种权衡是否是一种好的权衡。

(See 致力于学习)

您可以看到,正常操作节省的 CPU 随着我们的 减小深度。
但是我们也可以看到,随着深度的增加,节省的空间并不大。在10到50之间节省5-10% 可能是值得牺牲 CPU 的。把1% 的存款从50美元存到100美元,或者把0.5% 的存款从100美元存到250美元,可能就不行了。


说到 CPU 节省,“ git repack”学会了接受 --threads=<n>选项并将其传递给 pack-object。

提交40bcf31(2017年4月26日) by 滨野俊男(gitster)
(由 朱尼奥 · C · 哈马诺 gitster于2017年5月29日在 犯31fb6f4合并)

重新包装: 接受 --threads=<n>并将其传递给 pack-objects

我们已经为 --window=<n>--depth=<n>这样做了; 这将有所帮助 当用户想要强制 --threads=1进行重复性测试时 而不会受到多线程竞争的影响。

git gc --aggressive的问题在于,选项名称和文档有误导性。

作为 莱纳斯本人在这封信中解释道git gc --aggressive基本上是这样做的:

While git generally tries to re-use delta information (because it's a good idea, and it doesn't waste CPU time re-finding all the good deltas we found earlier), sometimes you want to say "let's start all over, with a blank slate, and ignore all the previous delta information, and try to generate a new set of deltas".

通常不需要在 git 中重新计算 delta,因为 git 确定这些 delta 非常灵活。只有当你知道自己有非常非常糟糕的三角洲时才有意义。正如 Linus 所解释的,主要使用 git fast-import的工具属于这一类。

大多数时候,git 在确定有用的 delta 方面做得相当不错,而使用 git gc --aggressive将使得 delta 更糟糕,同时浪费了大量 CPU 时间。


Linus 在邮件的最后得出结论: 在大多数情况下,带有大型 --depth--windowgit repack是更好的选择; 特别是在您导入了一个大型项目并希望确保 git 找到好的 delta 之后。

所以相当于 git gc --aggressive-但是完成了 适当地-就是(在一夜之间)这样做

git repack -a -d --depth=250 --window=250

深度的问题就是 delta 链有多深(让它们在过去的历史中变得更长——这值得花费大量的空间) ,窗口的问题就是我们希望每个 delta 候选者扫描的对象窗口有多大。

And here, you might well want to add the -f flag (which is the "drop all old deltas", since you now are actually trying to make sure that this one actually finds good candidates.

现在没有什么不同: git gc --aggressive是根据 Linus 在2007年提出的建议运行的; 见下文。在2.11版本(2016年第四季度)中,git 的默认深度为50。大小为250的窗口是好的,因为它扫描每个对象的更大部分,但是深度为250的窗口是坏的,因为它使每个链引用非常深的旧对象,这会减慢 所有未来 git 操作的速度,从而略微降低磁盘使用。


历史背景

Linus 建议(见下面的完整邮件列表文章)只有当你有,用他的话说,“一个 真的坏包”或“非常可怕的坏增量,”然而“几乎总是,在其他情况下,它实际上是一个非常糟糕的事情去做。”结果甚至可能使存储库处于比启动时更糟糕的状态!

在导入了“一段漫长而复杂的历史”之后,他建议正确地执行以下命令

git repack -a -d -f --depth=250 --window=250

但是这里假设您已经从存储库历史中获得了 去除多余的粘性物质,并且您已经按照检查表缩小了在 git filter-branch文档中找到的存储库。

git-filter-branch can be used to get rid of a subset of files, usually with some combination of --index-filter and --subdirectory-filter. People expect the resulting repository to be smaller than the original, but you need a few more steps to actually make it smaller, because Git tries hard not to lose your objects until you tell it to. First make sure that:

  • 如果一个文件块在其生存期内被移动,那么您实际上删除了文件名的所有变体。git log --name-only --follow --all -- filename可以帮助您找到重命名。

  • You really filtered all refs: use --tag-name-filter cat -- --all when calling git filter-branch.

然后有两种方法可以获得较小的存储库。更安全的方法是克隆,这样可以保持你原来的样子。

  • git clone file:///path/to/repo克隆它。克隆不会有已删除的对象。看看基特克隆。(请注意,使用普通路径的克隆只是硬链接所有内容!)

如果您确实不想克隆它,不管出于什么原因,请检查以下几点(按照这个顺序)。这是一种非常具有破坏性的方法,所以要么做一个备份,要么重新进行克隆。我已经警告过你了。

  • Remove the original refs backed up by git-filter-branch: say

    git for-each-ref --format="%(refname)" refs/original/ |
    xargs -n 1 git update-ref -d
    
  • Expire all reflogs with git reflog expire --expire=now --all.

  • Garbage collect all unreferenced objects with git gc --prune=now (or if your git gc is not new enough to support arguments to --prune, use git repack -ad; git prune instead).


Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST)
From: Linus Torvalds <torvalds at linux-foundation dot org>
To: Daniel Berlin <dberlin at dberlin dot org>
cc: David Miller <davem at davemloft dot net>,
ismail at pardus dot org dot tr,
gcc at gcc dot gnu dot org,
git at vger dot kernel dot org
Subject: Re: Git and GCC
In-Reply-To: <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>
Message-ID: <alpine.LFD.0.9999.0712052132450.13796@woody.linux-foundation.org>
References: <4aca3dc20712051947t5fbbb383ua1727c652eb25d7e@mail.gmail.com>
<20071205.202047.58135920.davem@davemloft.net>
<4aca3dc20712052032n521c344cla07a5df1f2c26cb8@mail.gmail.com>
<20071205.204848.227521641.davem@davemloft.net>
<4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>

2007年12月6日星期四,丹尼尔 · 柏林写道:

事实上,原来 git-gc --aggressive是做这种蠢事的 有时打包文件,而不管您是否从 不管是不是 SVN 回购。

当然。 git --aggressive基本上是哑的。它真的只有在 例如“我知道我有一个 真的坏包,我想把它扔掉 我所做过的所有糟糕的包装决定。”

为了解释这一点,这是值得解释的(您可能已经意识到了这一点,但是 不管怎样,让我来介绍一下基础知识) git delta 链是如何工作的,以及它是如何工作的 它们与大多数其他系统有很大的不同。

在其他供应链管理中,三角链通常是固定的,它可能是“正向”的 或者“向后”,当您使用存储库时,它可能会发生一些变化, 但通常是对单个文件的一连串修改 kind of single SCM entity. In CVS, it’s obviously the *,v file, and a lot 其他系统做相似的事情。

Git 也做三角形链,但是它做得更“松散” is no fixed entity. Deltas are generated against any random other version Git 认为是一个很好的 delta 候选人(具有各种各样的 成功的启发式) ,绝对没有硬分组规则。

这通常是一件非常好的事情,对各种概念都有好处 原因(也就是说。,git 内部从来没有真正需要关心整个 修正链ーー它实际上根本不考虑增量) ,但是 这也很棒,因为摆脱僵化的 delta 规则意味着 那个 git 在合并两个文件时没有任何问题, 例如ーー根本没有任意的 *,v“修订文件”具有 一些隐藏的意义。

这也意味着,对 delta 的选择要开放得多 如果您将增量链限制为一个文件,那么您真的不需要 有很多选择,如何处理 delta,但在 git 中,它真的 can be a totally different issue.

这就是名字不好的 --aggressive的用武之地 Git 通常尝试重用 delta 信息(因为这是一个好主意, 它不浪费 CPU 时间重新找到我们找到的所有良好的增量 有时候你想说“让我们重新开始,用一个空格。” 并忽略以前的所有 delta 信息,然后尝试生成 一组新的三角洲。”

所以 --aggressive并不是真正意义上的攻击性,而是浪费 CPU 时间重新做一个我们之前已经做过的决定!

有时这是一件好事,特别是一些导入工具 产生非常可怕的坏的增量。任何使用 git fast-import的东西, 例如,可能没有很好的 delta 布局,所以它可能 值得一提的是“我想重新开始”

但几乎总是,在其他情况下,这实际上是一件非常糟糕的事情。 这将浪费 CPU 时间,特别是如果您实际上已经完成了 做得好的删除早,最终的结果是不会重复使用所有 这些 很好三角洲你已经找到了,所以你实际上会得到一个 更糟糕的结果了!

我会发一个补丁到 Junio 只是删除 abc 0 它可能是有用的,但通常只有在您 真正深刻地理解它在做什么 文件并不能帮助你做到这一点。

一般来说,增量 git gc是正确的方法,而且效果更好 比做 git gc --aggressive。它将重新使用旧的三角洲,和 当那些旧的增量找不到的时候(增量 GC 的原因) 首先!)它将创造新的。

另一方面,“初始导入一个长的 这是一个值得花费大量金钱的时刻 找到 非常好增量的时间。然后,每个用户以后(作为 只要他们不使用 git gc --aggressive来撤消它!)将得到 一次性事件的优势。所以特别是对于大型项目 历史悠久,可能值得做一些额外的工作,告诉三角洲 找到疯狂的代码。

So the equivalent of git gc --aggressive — but done 适当地 — is to (在夜间)做某事

git repack -a -d --depth=250 --window=250

深度是指三角洲链的深度 (make them longer for old history — it’s worth the space overhead), and 窗口是关于对象窗口的大小,我们需要每个 delta 扫描候选人。

在这里,您可能希望添加 -f标志(即“ drop all “旧三角洲”因为你们现在要确保 actually finds good candidates.

然后,这将需要永远和一天(也就是说。,一个“做了一夜” thing). But the end result is that everybody downstream from that 存储库将获得更好的包,而不需要花费任何努力 他们自己。

          Linus

注意: 如果没有备份,不要使用没有与远程同步的存储库运行 git gc --agressive

此操作从头开始重新创建增量,如果中断得当,可能会导致数据丢失。

对于我的8GB 计算机,1Gb 存储库中的 gc 在10k 小提交的情况下耗尽了内存。当 OOM 杀手终止了 Git 进程——它留给我的存储库几乎是空的,只有工作树和少数 delta 存活下来。

当然,它不是存储库的唯一副本,所以我只是重新创建它并从远程提取(在破碎的回购中获取不起作用,并且在“解析 delta”步骤中死锁了几次) ,但是如果你的回购是单个开发者的本地回购,根本没有远程——首先备份它。

注意: 正如 Git 2.22(2019年第二季度)所阐明的,要注意使用 git gc --aggressive

参见 提交0044f77提交 daecbf2commit 7384504犯22d4e3b提交080a448犯下54d56f5提交 d257e0f犯下 b6a8d09(2019年4月7日)和 提交 fc559fb提交0044f770,提交0044f771(2019年3月22日)。
(Merged by 朱尼奥 · C · 哈马诺 gitster in 行动, 25 Apr 2019)

gc文档: 淡化 --aggressive的有用性

现有的“ gc --aggressive”文档只是短暂的建议 users that they run it regularly.
我个人与许多使用这些文档作为使用这个选项的建议的用户进行了交谈,他们通常使用 (大多数情况下)这是浪费时间

因此,让我们弄清楚它到底是做什么的,并让用户得出自己的结论。

我们还要阐明“效果[ ... ]是持久的”来解释 Jeff King 的解释的一个简短版本。

这意味着 git-gc documentation now includes:

有侵略性

当提供 --aggressive选项时,将使用 -f标志调用 git-repack,而 -f标志又将 --no-reuse-delta传递给 Git-pack-Objects
这将抛弃所有现有的 delta 并重新计算它们,代价是 花更多的时间重新包装。

这样做的影响是持久的,例如当包和松散对象合并成一个包时,该包中现有的 delta 可能会被重用,但也有各种情况下,我们可能会从一个新的包中选择一个次优 delta 来代替。

此外,提供 --aggressive将调整传递给 git-repack--depth--window选项。
请参阅下面的 gc.aggressiveDepthgc.aggressiveWindow设置。
通过使用更大的窗口大小,我们更有可能找到更优的三角洲。

It's probably not worth it to use this option on a given repository without running tailored performance benchmarks on it.
这需要花费更多的时间,并且由此产生的空间/增量优化可能值得,也可能不值得。对于大多数用户及其存储库来说,根本不使用这种方法是正确的权衡。

以及(提交080a448) :

gc文档: 注意 --aggressive如何影响 --window--depth

由于 07e7dbf(gc: 默认的侵略深度为50,2016-08-11,Git v2.10.1) ,我们在 --aggressive下使用的深度与默认情况下使用的深度有些混淆。

正如提交中指出的那样,将更多深度作为“主动”的默认值是错误的,从而以牺牲运行时性能为代价来节省磁盘空间,这通常与喜欢“主动 gc”的人的想法相反。