我正在寻找减少git
存储库大小的方法.搜索引导我到git gc --aggressive
大多数时间.我还读到这不是首选方法.
为什么?如果我跑步,我应该注意什么gc --aggressive
?
git repack -a -d --depth=250 --window=250
推荐结束gc --aggressive
.为什么?如何repack
减少存储库的大小?此外,我不太清楚旗帜--depth
和--window
.
我应该怎么选择gc
和repack
?什么时候应该使用gc
和repack
?
1> Greg Bacon..:
如今没有区别:git gc --aggressive
根据Linus 2007年提出的建议运作; 见下文.从版本2.11(2016年第4季度)开始,git默认深度为50.大小为250的窗口是好的,因为它扫描每个对象的较大部分,但是250的深度是坏的,因为它使每个链都指向非常深的旧对象,这会减慢所有未来的git操作,从而降低磁盘使用率.
历史背景
Linus建议(参见下面的完整邮件列表帖子),git gc --aggressive
只有当你用他的话说,"一个非常糟糕的包"或"非常糟糕的三角洲",然而"几乎总是,在其他情况下,它实际上是一个非常糟糕的要做的事情."结果甚至可能使您的存储库处于比您开始时更糟糕的状态!
他建议在导入"漫长而复杂的历史"之后正确地执行此操作的命令是
git repack -a -d -f --depth=250 --window=250
但是这假设您已经从存储库历史记录中删除了不需要的gunk,并且您已经按照清单来缩小git filter-branch
文档中找到的存储库.
git的过滤分支可以用来摆脱文件的一个子集,通常用的一些组合--index-filter
和--subdirectory-filter
.人们期望生成的存储库小于原始存储库,但是你需要更多的步骤来实际使它变小,因为Git努力不会丢失你的对象,直到你告诉它.首先要确保:
如果blob在其生命周期内被移动,那么您确实删除了文件名的所有变体.git log --name-only --follow --all -- filename
可以帮助您找到重命名.
你真的过滤了所有的参考:--tag-name-filter cat -- --all
在通话时使用git filter-branch
.
然后有两种方法可以获得更小的存储库.更安全的方法是克隆,保持原始原封不动.
克隆它git clone file:///path/to/repo
.克隆将没有删除的对象.见git-clone.(请注意,使用普通路径克隆只是硬连接所有内容!)
如果你真的不想克隆它,无论出于何种原因,请检查以下几点(按此顺序).这是一种非常具有破坏性的方法,因此请进行备份或返回克隆它.你被警告了.
删除git-filter-branch备份的原始引用:比如说
git for-each-ref --format="%(refname)" refs/original/ |
xargs -n 1 git update-ref -d
使用的所有reflog到期git reflog expire --expire=now --all
.
垃圾收集所有未引用的对象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
To: Daniel Berlin
cc: David Miller ,
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:
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-chains如何工作,以及它们如何与大多数其他系统如此不同.
在其他SCM中,delta链通常是固定的.它可能是"前进"或"后退",并且在您使用存储库时它可能会有所改进,但通常它是对单个文件的一系列更改,表示为某种单个SCM实体.在CVS中,它显然是*,v
文件,很多其他系统都做类似的事情.
Git也做delta链,但它更"松散"地做了它们.没有固定的实体.Delt是针对git认为是一个优秀的delta候选者的任何随机其他版本生成的(具有各种相当成功的启发式算法),并且绝对没有硬分组规则.
这通常是一件非常好的事情.它有各种概念上的好处(也就是说,git内部从来都不需要关心整个修订链 - 它根本没有根据增量来考虑),但它也很棒,因为摆脱不灵活的delta规则意味着例如,git没有任何问题将两个文件合并在一起 - 根本就没有任意*,v
隐藏含义的"修订文件".
这也意味着增加选择是一个更开放的问题.如果你将delta链限制为一个文件,你真的没有很多关于如何处理增量的选择,但在git中,它确实是一个完全不同的问题.
这就是真正命名--aggressive
的地方.虽然git通常会尝试重新使用delta信息(因为它是一个好主意,并且它不会浪费CPU时间重新找到我们之前发现的所有好的增量),有时你我想说"让我们从头开始,用空白的石板,忽略所有以前的增量信息,并尝试生成一组新的增量."
所以--aggressive
不是真正的积极进取,而是浪费CPU时间重新做出我们之前做过的决定!
有时这是件好事.特别是一些导入工具可能会产生非常糟糕的增量.git fast-import
例如,任何使用的东西都可能没有太大的三角洲布局,因此值得说"我想从一个干净的平板开始".
但几乎总是,在其他情况下,这实际上是一件非常糟糕的事情.它会浪费CPU时间,特别是如果你之前在deltaing中做得很好,最终的结果是不会重复使用你已经找到的那些好的增量,所以你实际上最终会得到很多更糟糕的最终结果!
我将向Junio发送补丁以删除git gc --aggressive
文档.它可能很有用,但它通常只有在你真正理解它正在做的事情时才有用,并且文档对你没有帮助.
一般来说,做增量git gc
是正确的方法,而不是做git gc --aggressive
.它将重新使用旧的增量,当无法找到那些旧的增量时(首先进行增量GC的原因!)它将创建新的增量.
另一方面,"长期参与历史的最初导入"绝对是一个值得花费大量时间寻找真正好的增量的点.然后,每个用户之后(只要他们不用git gc --aggressive
来撤消它!)将获得该一次性事件的优势.因此,特别是对于历史悠久的大型项目,可能值得做一些额外的工作,告诉delta发现代码疯狂.
因此,相当于git gc --aggressive
- 但做得恰当 - 是(过夜)类似的事情
git repack -a -d --depth=250 --window=250
那个深度的东西只是关于三角链的深度(让旧历史更长一些 - 它值得空间开销),窗口的事情是我们希望每个delta候选者扫描多大的对象窗口.
在这里,您可能想要添加-f
标志(这是"放弃所有旧的增量",因为您现在实际上正在尝试确保这个实际上找到了好的候选者.
然后它将需要永远和一天(即 "一夜之间"的事情).但最终的结果是,该存储库下游的所有人都将获得更好的包,而不必自己花费任何精力.
Linus
您对深度的评论有点令人困惑。刚开始我会抱怨你错了,因为积极进取可以大大加快git存储库的速度。在进行了积极的垃圾收集之后,一个巨大的仓库花了五分钟的时间才变成git状态,减少到几秒钟。但是后来我意识到您并不是说侵略性的gc减慢了回购速度,而是很大的深度。
2> VonC..:
我什么时候应该使用gc&repack?
正如我在" Git垃圾收集似乎没有完全发挥作用 "中提到的那样,a git gc --aggressive
本身就不够,甚至不够.
最有效的组合是添加git repack
,但也git prune
:
git gc
git repack -Ad # kills in-pack garbage
git prune # kills loose garbage
注意:Git 2.11(2016年第4季度)会将默认gc激进深度设置为50
请参阅Jeff King()提交07e7dbf(2016年8月11日).(由Junio C Hamano合并- -在提交0952ca8,2016年9月21日)gc aggressive
peff
gitster
:默认积极深度为50
" gc
"用于将delta-chain长度限制为250,这对于获得额外的空间节省而言太深,并且对运行时性能不利.
限制已降至50.
总结是:当前默认值250不会节省太多空间,并且会花费CPU.这不是一个很好的权衡.
" git gc --aggressive
"标志--aggressive
做三件事:
使用" git-gc
"抛弃现有的增量并从头开始重新计算
使用"--window = 250"来增加增量效果
使用"--depth = 250"来制作更长的三角链
项目(1)和(2)是"积极"重新包装的良好匹配.
他们要求重新包装做更多的计算工作,以期获得更好的包装.您在重新包装期间支付成本,而其他操作仅看到好处.
第(3)项不太清楚.
允许更长的链意味着对增量的限制更少,这意味着可能找到更好的链并节省一些空间.
但这也意味着访问增量的操作必须遵循更长的链,这会影响它们的性能.
所以这是一个权衡,并且不清楚这种权衡甚至是一个好的权衡.
(参见研究承诺)
您可以看到,随着我们减小深度,常规操作的CPU节省会得到改善.
但我们也可以看到,随着深度的增加,节省的空间并不是那么大.在10到50之间节省5-10%可能值得CPU权衡.节省1%从50到100,或另外0.5%从100到250可能不是.
说到CPU保存," -f
"学会接受该git repack
选项并将其传递给pack-objects.
请参阅Junio C Hamano()提交的40bcf31(2017年4月26日).(由Junio C Hamano合并- -在2017年5月29日的提交31fb6f4中)--threads=
gitster
重新包装:接受gitster
并传递给--threads=
我们已经对这样做的pack-objects
和--window=
; 当用户想要强制--depth=
进行可重复的测试而不受赛车多线程的影响时,这将有所帮助.
我在"Git Garbage集合似乎没有完全发挥作用"链接中提到了Linus线程
3> Sascha Wolf..:
问题git gc --aggressive
是选项名称和文档具有误导性.
正如Linus本人在这封邮件中解释的那样,git gc --aggressive
基本上是这样的:
虽然git通常会尝试重新使用delta信息(因为它是一个好主意,并且它不会浪费CPU时间重新找到我们之前发现的所有好的增量),有时你想说"让我们从头开始,用一个空白石板,并忽略所有以前的增量信息,并尝试生成一组新的增量".
通常不需要在git中重新计算增量,因为git确定这些增量非常灵活.只有你知道你有非常非常糟糕的增量才有意义.正如Linus所解释的那样,主要使用的工具git fast-import
属于这一类.
大多数时候git在确定有用的增量方面表现相当不错,并且使用git gc --aggressive
会留下增量,这可能会在浪费大量CPU时间的情况下更糟糕.
莱纳斯结束了他的邮件与结论,即git repack
用大--depth
而--window
在大多数时间是更好的选择; 特别是在你导入一个大项目并想确保git找到好的增量之后.
因此,相当于git gc --aggressive
- 但做得恰当 - 是(过夜)类似的事情
git repack -a -d --depth=250 --window=250
那个深度的东西只是关于三角链的深度(让旧历史更长一些 - 它值得空间开销),窗口的事情是我们希望每个delta候选者扫描多大的对象窗口.
在这里,你可能想要添加-f
标志(这是"丢弃所有旧的增量",因为你现在实际上是在努力确保这个实际上找到了好的候选者.
4> Sage Pointer..:
警告.git gc --agressive
如果没有备份,请不要使用与远程同步的存储库运行.
此操作从头开始重新创建增量,如果正常中断,可能会导致数据丢失.
对于我的8GB计算机,攻击性gc在1Gb存储库上运行内存不足10k次.当OOM杀手终止git进程时 - 它给我留下了几乎空的存储库,只有工作树和少数三角洲幸存下来.
当然,它不是存储库的唯一副本,所以我只是重新创建它并从远程拉出(fetch不能用于破坏的回购并且在'解决增量'步骤上遇到了很多次我试图这样做),但如果你的回购是没有遥控器的单开发商本地仓库 - 首先备份它.