git gc --aggressive vs git repack
Posted
技术标签:
【中文标题】git gc --aggressive vs git repack【英文标题】: 【发布时间】:2015-04-27 12:19:38 【问题描述】:我正在寻找减小git
存储库大小的方法。大多数情况下,搜索会将我带到git gc --aggressive
。我还读到这不是首选方法。
为什么?如果我正在运行gc --aggressive
,我应该注意什么?
推荐git repack -a -d --depth=250 --window=250
优于gc --aggressive
。为什么? repack
如何减小存储库的大小?另外,我不太清楚--depth
和--window
的标志。
我应该在gc
和repack
之间选择什么?我什么时候应该使用gc
和repack
?
【问题讨论】:
【参考方案1】:现在没有区别:git gc --aggressive
按照Linus在2007年提出的建议运行;见下文。从 2.11 版(2016 年第四季度)开始,git 默认深度为 50。大小为 250 的窗口很好,因为它扫描每个对象的较大部分,但是 250 的深度不好,因为它使每个链都引用非常深的旧对象,这会减慢 所有 未来的 git 操作,从而略微降低磁盘使用率。
历史背景
Linus 建议(请参阅下面的完整邮件列表帖子)使用 git gc --aggressive
,用他的话来说,只有当你有“一个 非常 糟糕的包”或“非常糟糕的 deltas”,然而“几乎总是,在其他情况下,这实际上是一件非常糟糕的事情。”结果甚至可能使您的存储库处于比您开始时更糟糕的状态!
他建议在导入“漫长而复杂的历史”后正确执行此操作的命令是
git repack -a -d -f --depth=250 --window=250
但这假设您已经从存储库历史记录中获得了removed unwanted gunk,并且您已按照在git filter-branch
documentation 中找到的用于缩小存储库的清单进行操作。
git-filter-branch 可用于删除文件子集,通常使用
--index-filter
和--subdirectory-filter
的某种组合。人们期望生成的存储库比原始存储库小,但您需要更多步骤才能真正将其缩小,因为 Git 会努力不丢失您的对象,直到您告诉它这样做。首先确保:如果 blob 在其生命周期内被移动,您确实删除了文件名的所有变体。
git log --name-only --follow --all -- filename
可以帮你找到重命名。您确实过滤了所有引用:在调用
git filter-branch
时使用--tag-name-filter cat -- --all
。那么有两种方法可以获得更小的存储库。一种更安全的方法是克隆,这样可以保持您的原件完好无损。
用git clone file:///path/to/repo
克隆它。克隆不会有被移除的对象。请参阅 git 克隆。 (请注意,使用普通路径进行克隆只会硬链接所有内容!)如果您真的不想克隆它,无论出于何种原因,请检查以下几点(按此顺序)。这是一种非常具有破坏性的方法,因此请进行备份或重新克隆它。您已收到警告。
删除由 git-filter-branch 备份的原始 refs:say
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
使用
git reflog expire --expire=now --all
使所有 reflog 过期。垃圾收集使用
git gc --prune=now
的所有未引用对象(或者如果您的git gc
不够新,无法支持--prune
的参数,请改用git repack -ad; git prune
)。
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 日星期四,Daniel Berlin 写道:
其实,原来
git-gc --aggressive
做了这件蠢事 有时打包文件,无论您是否从 SVN repo 与否。当然。
git --aggressive
大多是愚蠢的。它真的只对 “我知道我有一个真的坏包,我想扔掉 我做过的所有糟糕的包装决定。”为了解释这一点,值得解释(你可能知道,但是 无论如何,让我了解一下基础知识)git delta-chains 是如何工作的,以及如何 它们与大多数其他系统非常不同。
在其他 SCM 中,delta 链通常是固定的。可能是“前锋” 或“向后”,当您使用存储库时,它可能会有所发展, 但通常它是对单个文件的一系列更改,表示为一些 一种单一的 SCM 实体。在 CVS 中,很明显是
*,v
文件,还有很多 的其他系统做类似的事情。Git 也做 delta-chains,但它做起来更“松散”。那里 不是固定的实体。 Deltas 是针对任何随机的其他版本生成的 git 认为是一个很好的 delta 候选者(有各种相当 成功的启发式),并且绝对没有硬性的分组规则。
这通常是一件非常好的事情。它适用于各种概念 原因(即,git在内部从来没有真正需要关心整体 修订链——它根本不考虑增量),但是 这也很棒,因为摆脱不灵活的增量规则意味着 git 在合并两个文件时完全没有任何问题, 例如——根本没有任意的
*,v
“修订文件”有 一些隐藏的含义。这也意味着增量的选择更加开放 题。如果您将增量链限制为仅一个文件,您真的不会 关于如何处理增量有很多选择,但在 git 中,它真的 可能是一个完全不同的问题。
这就是真正糟糕的
--aggressive
的用武之地。虽然 git 通常会尝试重用 delta 信息(因为这是个好主意, 并且它不会浪费 CPU 时间重新找到我们找到的所有好的增量 较早),有时你想说“让我们从头开始,留个空白 slate,并忽略所有先前的 delta 信息,并尝试生成 一组新的增量。”所以
--aggressive
并不是真的要咄咄逼人,而是要浪费 CPU 时间重新做我们之前已经做过的决定!有时这是一件好事。一些导入工具尤其可以 产生非常糟糕的三角洲。任何使用
git fast-import
的东西, 例如,可能没有很好的 delta 布局,所以它可能 值得说的是“我想从头开始。”但几乎总是,在其他情况下,这实际上是一件非常糟糕的事情。 这会浪费 CPU 时间,尤其是如果你真的做了一个 早点完成增量工作,最终结果不会重用所有 你已经找到了那些 good 增量,所以你最终会得到一个 更糟糕的最终结果!
我会向 Junio 发送一个补丁来删除
git gc --aggressive
文档。它可能很有用,但通常只有当你 真正深入了解它在做什么,并且 文档并不能帮助您做到这一点。一般来说,做增量
git gc
是正确的方法,而且更好 比做git gc --aggressive
。它将重新使用旧的增量,并且 当那些旧的 deltas 找不到时(进行增量 GC 的原因 首先!)它将创建新的。另一方面,“长 和涉及的历史”是值得花很多钱的地方 是时候找到非常好的增量了。然后,以后的每个用户(如 只要他们不使用
git gc --aggressive
撤消它!)将获得 一次性事件的优势。因此,特别是对于具有 历史悠久,可能值得做一些额外的工作,告诉三角洲 寻找疯狂的代码。所以
git gc --aggressive
的等价物——但完成正确——是 做(一夜之间)类似的事情git repack -a -d --depth=250 --window=250
深度是指 delta 链的深度 (让它们在旧历史中更长——值得开销的空间),和 窗口问题是关于我们希望每个增量有多大的对象窗口 扫描的候选人。
在这里,您可能想要添加
-f
标志(即“全部删除 旧三角洲,”因为您现在实际上是在尝试确保这个 实际上找到了好的候选人。然后这将需要永远和一天(即,“一夜之间完成” 事物)。但最终的结果是,下游的每个人 存储库将获得更好的包,而无需花费任何精力 自己动手。
Linus
【讨论】:
您对深度的评论有点令人困惑。起初我要抱怨你大错特错,激进可以大大加快 git 存储库的速度。在进行了一次积极的垃圾收集之后,一个巨大的 repo 需要五分钟才能完成 git status 减少到几秒钟。但后来我意识到你并不是说激进的 gc 减慢了回购的速度,而只是一个非常大的深度大小。【参考方案2】:什么时候应该使用 gc & repack?
正如我在“Git Garbage collection doesn't seem to fully work”中提到的,git gc --aggressive
本身还不够,甚至不够。
而且,作为I explain below,通常不需要。
最有效的组合是添加git repack
,但也添加git prune
:
git gc
git repack -Ad # kills in-pack garbage
git prune # kills loose garbage
注意:Git 2.11(2016 年第四季度)会将默认的 gc aggressive
深度设置为 50
参见Jeff King (peff
) 的commit 07e7dbf(2016 年 8 月 11 日)。(由 Junio C Hamano -- gitster
-- 合并于 commit 0952ca8,2016 年 9 月 21 日)
gc
:默认进取深度为 50"
git gc --aggressive
" 用于将 delta-chain 长度限制为 250, 这对于获得额外的空间节省来说太深了,并且是 不利于运行时性能。 限制已降至 50。总结是:当前默认的 250 没有 节省大量空间,并消耗CPU。这不是一个好的权衡。
git-gc
的“--aggressive
”标志做了三件事:使用“
-f
”丢弃现有的增量并从头开始重新计算 使用“--window=250”更仔细地查找增量 使用“--depth=250”来制作更长的三角链项目 (1) 和 (2) 非常适合“激进”重新包装。 他们要求重新包装做更多的计算工作,以期获得更好的包装。您在重新包装期间支付费用,而其他操作只看到收益。
第 (3) 项不太清楚。 允许更长的链意味着对增量的限制更少,这意味着可能会找到更好的链并节省一些空间。 但这也意味着访问增量的操作必须遵循更长的链,这会影响它们的性能。 所以这是一种折衷,而且这种折衷是否是好的还不清楚。
(见commit for study)
您可以看到,常规操作的 CPU 节省随着我们的改进而提高 减少深度。 但我们也可以看到,随着深度的增加,节省的空间并没有那么大。在 10 到 50 之间节省 5-10% 可能值得 CPU 权衡。从 50 到 100 节省 1%,或者从 100 到 250 再节省 0.5% 可能不是。
说到节省 CPU,“git repack
”学会了接受 --threads=<n>
选项并将其传递给 pack-objects。
参见Junio C Hamano (gitster
) 的commit 40bcf31(2017 年 4 月 26 日)。(由 Junio C Hamano -- gitster
-- 合并于 commit 31fb6f4,2017 年 5 月 29 日)
重新打包:接受
--threads=<n>
并将其传递给pack-objects
我们已经为--window=<n>
和--depth=<n>
这样做了;这会有所帮助
当用户想要强制--threads=1
进行可重复测试时
不受多线程竞争的影响。
【讨论】:
我在“Git 垃圾收集似乎无法完全正常工作”链接中提到了 Linus 线程 感谢的现代更新!这里的所有其他答案都是旧的。现在我们可以看到git gc --aggressive
已经被修复了两次:首先,按照Linus 在2007 年提出的“更好的打包方法”来做。然后在 Git 2.11 中避免 Linus 建议的过度对象深度,但事实证明这是有害的(减慢所有未来的 Git 操作并且没有节省任何值得一提的空间)。
git gc ,然后是 git repack -Ad 和 git prune 增加了我的存储库的大小......为什么?
@devops 不确定:您使用的是什么版本的 Git?您可以为此提出一个新问题(提供更多详细信息,例如操作系统、存储库的一般大小......)
man git-repack
对-d
说:`同时运行 git prune-packed 以删除多余的松散对象文件。` 或者git prune
也这样做? man git-prune
说的是In most cases, users should run git gc, which calls git prune.
,那么git gc
后面有什么用呢?只使用git repack -Ad && git gc
不是更好或足够吗?【参考方案3】:
git gc --aggressive
的问题在于选项名称和文档具有误导性。
作为Linus himself explains in this mail,git gc --aggressive
的基本工作是这样的:
虽然 git 通常会尝试重用 delta 信息(因为这是一个好主意,并且不会浪费 CPU 时间来重新查找我们之前找到的所有好的 delta),但有时你想说“让我们重新开始, 用一张白纸, 并忽略所有以前的增量信息, 并尝试生成一组新的增量”。
通常不需要在 git 中重新计算增量,因为 git 非常灵活地确定这些增量。只有当你知道你有非常非常糟糕的增量时,它才有意义。正如 Linus 所解释的,主要使用 git fast-import
的工具属于这一类。
大多数时候 git 在确定有用的增量方面做得很好,使用 git gc --aggressive
会给您留下可能更糟的增量,同时浪费大量 CPU 时间。
Linus 在结束他的邮件时得出的结论是,git repack
与一个大的--depth
和--window
在大多数情况下是更好的选择;尤其是在您导入了一个大型项目并希望确保 git 找到好的增量之后。
所以相当于
git gc --aggressive
- 但完成正确 - 是(一夜之间)做类似的事情
git repack -a -d --depth=250 --window=250
深度是关于增量链可以有多深(使它们在旧历史中更长 - 它值得空间开销),而窗口是关于我们希望每个增量候选者扫描的对象窗口有多大.
在这里,您可能想要添加
-f
标志(即“删除所有旧的增量”,因为您现在实际上是在尝试确保这个确实找到了好的候选人。
【讨论】:
【参考方案4】:小心。如果您没有备份,请不要使用未与远程同步的存储库运行 git gc --agressive
。
此操作从头开始重新创建增量,如果正常中断可能会导致数据丢失。
对于我的 8GB 计算机,激进 gc 在 1Gb 存储库上用 10k 个小提交耗尽了内存。当 OOM 杀手终止 git 进程时 - 它给我留下了几乎空的存储库,只有工作树和少数 deltas 幸存下来。
当然,它不是存储库的唯一副本,所以我只是重新创建它并从远程拉取(fetch 在损坏的 repo 上不起作用,并且在我尝试这样做的几次'resolving deltas'步骤中陷入僵局),但是如果您的 repo 是完全没有遥控器的单一开发人员本地 repo - 首先备份它。
【讨论】:
【参考方案5】:注意:请注意使用 git gc --aggressive
,正如 Git 2.22(2019 年第二季度)所阐明的那样。
见commit 0044f77、commit daecbf2、commit 7384504、commit 22d4e3b、commit 080a448、commit 54d56f5、commit d257e0f、commit b6a8d09(2019 年 4 月 7 日)和commit fc559fb、@987@ commit b11e856(2019 年 3 月 22 日)Ævar Arnfjörð Bjarmason (avar
)。(由 Junio C Hamano -- gitster
-- 合并于 commit ac70c53,2019 年 4 月 25 日)
gc
docs:淡化--aggressive
的用处现有的“
gc --aggressive
”文档没有推荐到 定期运行它的用户。 我亲自与许多用户交谈过,他们将这些文档作为使用此选项的建议,并且通常(大部分)是浪费时间。所以让我们澄清一下它的真正作用,让用户自己得出结论。
让我们也澄清一下“效果 [...] 是持久的”,以解释 Jeff King's explanation 的简短版本。
这意味着git-gc documentation now includes:
激进
当提供
--aggressive
选项时,将使用-f
标志调用git-repack
,然后将--no-reuse-delta
传递给git-pack-objects。 这将丢弃任何现有的增量并重新计算它们,代价是 在重新包装上花费更多时间。这种影响大多是持久的,例如当包和松散的对象合并到另一个包中时,该包中的现有增量可能会被重新使用,但也有各种情况,我们可能会从较新的包中选择一个次优的增量。
此外,提供
--aggressive
将调整传递给git-repack
的--depth
和--window
选项。 请参阅下面的gc.aggressiveDepth
和gc.aggressiveWindow
设置。 通过使用更大的窗口大小,我们更有可能找到更优化的增量。如果不对给定存储库运行定制的性能基准测试,可能不值得在给定存储库上使用此选项。 这需要更多时间,由此产生的空间/增量优化可能值得也可能不值得。对于大多数用户及其存储库而言,完全不使用它是正确的权衡。
还有(commit 080a448):
gc
文档:注意--aggressive
如何影响--window
和--depth
由于07e7dbf(
gc
:默认进取深度为 50,2016 年 8 月 11 日,Git v2.10.1)我们在--aggressive
下使用与默认相同的深度有点令人困惑。正如在那个有意义的提交中指出的那样,将更多深度设置为“积极”的默认值是错误的,从而以牺牲运行时性能为代价来节省磁盘空间,这通常与喜欢“积极的 gc" 想要。
【讨论】:
以上是关于git gc --aggressive vs git repack的主要内容,如果未能解决你的问题,请参考以下文章