git gc --aggressive vs git repack

时间:2015-02-25 13:23:26

标签: git github version-control

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

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

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

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

5 个答案:

答案 0 :(得分:65)

如今没有区别: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

但是这假设您已经从存储库历史记录中removed unwanted gunk并且已经按照清单来缩小git filter-branch documentation中找到的存储库。

  

git-filter-branch可用于删除文件的子集,通常使用--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
    
  •   
  • 使用git reflog expire --expire=now --all暂停所有reflog。

  •   
  • 垃圾会使用git gc --prune=now收集所有未引用的对象(或者如果您的git gc不够新,无法支持--prune的参数,请改用git repack -ad; git prune。< / p>

  •   

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-chains如何工作,以及如何工作   它们与大多数其他系统截然不同。

     

在其他SCM中,delta链通常是固定的。它可能是“前锋”   或者“向后”,当你使用存储库时,它可能会有所改进,   但一般来说,它是对单个文件的一系列更改   一种单一的SCM实体。在CVS中,它显然是*,v文件,而且很多   其他系统做的事情很相似。

     

Git也会做三角形链,但它更“宽松”地做了它们   不是固定的实体。 Deltas是针对任何随机的其他版本生成的   那个git认为是一个很好的delta候选者(各种公平的   成功的启发式方法),绝对没有硬分组规则。

     

这通常是一件非常好的事情。这对各种概念都有好处   原因(,git内部从来都不需要关心整体   修订链 - 它根本没有根据增量来考虑,但是   它也很棒,因为摆脱僵硬的delta规则意味着   通过将两个文件合并在一起,git完全没有任何问题,   例如 - 根本没有任意*,v“修订文件”   一些隐藏的意思。

     

这也意味着增加的选择更加开放   题。如果将增量链限制为仅一个文件,则实际上不会   关于如何处理增量有很多选择,但是在git中,确实如此   可能是一个完全不同的问题。

     

这就是真正命名为--aggressive的地方   git通常会尝试重用delta信息(因为这是一个好主意,   并且它不会浪费CPU时间来重新找到我们发现的所有好的增量   早些时候),有时你想说“让我们从头开始,一片空白   平板,并忽略所有以前的增量信息,并尝试生成   一套新的增量。“

     

所以--aggressive并不是真正的咄咄逼人,而是浪费   CPU时间重新做了我们之前做过的决定!

     

有时这是一件好事。特别是一些导入工具可以   产生非常糟糕的三角洲。任何使用git fast-import的东西,   例如,可能没有太多的大三角洲布局,所以它可能   值得一提的是“我想从干净的石板开始。”

     

但几乎总是,在其他情况下,这实际上是一件非常糟糕的事情。   这将浪费CPU时间,特别是如果你真的做了一个   早些时候做好三角形工作,最终的结果是不会重复使用全部   你已经找到的那些好的增量,所以你实际上最终会得到一个   更糟糕的最终结果!

     

我会向Junio发送一个补丁来删除git gc --aggressive   文档。它可能很有用,但它通常只在您使用时才有用   真正了解它正在做什么,以及那个   文档对你没有帮助。

     

通常,做增量git gc是正确的方法,而且更好   比做git gc --aggressive。它将重新使用旧的增量,并且   当那些旧的增量无法找到时(进行增量GC的原因)   首先!)它将创造新的。

     

另一方面,“长期的初始进口”肯定是正确的   并且涉及历史“是值得花费很多钱的一点   时间找到非常好的增量。然后,每个用户之后(如   只要他们不使用git gc --aggressive撤消它!)就会得到   这次一次性活动的优势。特别是对于有大量项目的大型项目   历史悠久,值得做一些额外的工作,告诉三角洲   找到疯狂的代码。

     

所以相当于git gc --aggressive - 但正确 - 就是   做(过夜)像

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

深度的东西只是三角洲链的深度   (让他们的历史更久 - 这是值得的空间开销),和   窗口的事情是关于我们想要每个delta的对象窗口有多大   候选人扫描。

     

在这里,你可能想要添加-f标志(这是“全部丢弃”   老三角洲,“因为你现在正在努力确保这一个   实际上找到了好的候选人。

     

然后它将需要永远和一天(即。,“一夜之间做”   事情)。但最终的结果是每个人都在下游   存储库将获得更好的包,而不必花费任何精力   在它自己。

          Linus

答案 1 :(得分:47)

  

我什么时候应该使用gc&amp;重新包装?

正如我在“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年第4季度)将默认gc aggressive深度设置为50

commit 07e7dbfJeff King (peff)(2016年8月11日) (由Junio C Hamano -- gitster --合并于commit 0952ca8,2016年9月21日)

  

gc:默认积极深度为50

     

git gc --aggressive”用于将delta-chain长度限制为250,   这对于获得额外的空间节省来说太深了   对运行时性能有害。
  限制已降至50。

     

摘要是:当前默认值为250   节省大量空间,并节省CPU成本。这不是一个很好的权衡。

     

--aggressive的“git-gc”标记有三件事:

     
      
  1. 使用“-f”抛弃现有的增量并从头开始重新计算
  2.   
  3. 使用“--window = 250”来增加增量效果
  4.   
  5. 使用“--depth = 250”来制作更长的delta链
  6.         

    项目(1)和(2)是“积极”重新包装的良好匹配   他们要求重新包装做更多的计算工作,以期获得更好的包装。您在重新包装期间支付成本,而其他操作仅看到好处。

         

    第(3)项不太清楚   允许更长的链意味着对增量的限制更少,这意味着可能找到更好的增量并节省一些空间   但这也意味着访问增量的操作必须遵循更长的链,这会影响它们的性能   所以这是一个权衡,并且不清楚这种权衡甚至是一个好的。

(见commit for study

  

您可以看到常规操作的CPU节省随着我们的增加而提高   减少深度。
  但我们也可以看到,随着深度的增加,节省的空间并不是那么大。在10到50之间节省5-10%可能值得CPU权衡。保存1%从50到100,或另外0.5%从100到250可能不是。


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

commit 40bcf31Junio C Hamano (gitster)(2017年4月26日) Junio C Hamano -- gitster --于2017年5月29日commit 31fb6f4合并)

  

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

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

答案 2 :(得分:13)

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

作为Linus himself explains in this mailgit gc --aggressive基本上是这样做的:

  

虽然git通常会尝试重新使用delta信息(因为这是一个好主意,并且它不会浪费CPU时间重新找到我们之前发现的所有优秀增量),有时你想说“让我们从头开始,使用空白平板,并忽略所有先前的增量信息,并尝试生成一组新的增量“。

通常不需要在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

     

这个深度的东西只是关于delta链的深度(让它们在旧历史中更长 - 它值得开销空间),窗口的事情是我们希望每个delta候选者扫描的对象窗口有多大

     

在这里,你可能想要添加-f标志(这是“丢弃所有旧的增量”,因为你现在实际上是在努力确保这个实际上找到了好的候选者。

答案 3 :(得分:6)

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

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

对于我的8GB计算机,攻击性gc在1Gb存储库上的内存不足,提交了10k次小提交。当OOM杀手终止git进程时 - 它给我留下了几乎空的存储库,只有工作树和少数三角洲幸存下来。

当然,它不是存储库的唯一副本,所以我只是重新创建它并从远程拉出(fetch不能用于破坏的回购并且在解决增量问题上陷入僵局&#39;步骤几次我试图做所以),但如果你的回购是完全没有遥控器的单开发商本地回购 - 首先备份它。

答案 4 :(得分:0)

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

请参见commit 0044f77commit daecbf2commit 7384504commit 22d4e3bcommit 080a448commit 54d56f5commit d257e0f,{{3} }(2019年4月7日)和commit b6a8d09之前的commit fc559fbcommit cf9cd77commit b11e856(2019年3月22日)。
(由Ævar Arnfjörð Bjarmason (avar)Junio C Hamano -- gitster --中合并,2019年4月25日)

  

gc文档:淡化--aggressive

的有用性      

现有的“ gc --aggressive”文档缺少建议   用户定期运行它。
  我已经亲自与许多用户进行了交谈,这些用户已经将这些文档作为使用此选项的建议,并且(通常)是在浪费时间

     

因此,让我们澄清一下它的真正作用,然后让用户得出自己的结论。

     

让我们也澄清一下“效果是持久的”,以简述commit ac70c53的形式。

这意味着Jeff King's explanation

  

侵略性

     

提供--aggressive选项时,将使用-f标志调用git-gc documentation now includes,该标志又将--no-reuse-delta传递给git-repack
  这将丢弃所有现有的增量并重新计算它们,但代价是   在重新包装上花费更多的时间。

     

此效果主要是持久的,例如当将包和松散的对象合并到另一个包中时,该包中的现有增量可能会被重复使用,但是在许多情况下,我们可能会从较新的包中选择次优的增量。

     

此外,提供--aggressive将调整传递给git-pack-objects--depth--window选项。
  请参阅下面的gc.aggressiveDepthgc.aggressiveWindow设置。
  通过使用更大的窗口尺寸,我们更有可能找到更多的最佳增量。

     

在给定的存储库上不运行定制的性能基准的情况下,在该存储库上使用此选项可能不值得
  这需要花费更多时间,并且最终的空间/增量优化可能值得也可能不值得。对于大多数用户及其存储库,完全不使用此功能是正确的权衡。

然后(git-repack):

  

gc文档:请注意--aggressive--window--depth的影响

     

由于commit 080a448gc:默认进阶深度为50,2016-08-11,Git v2.10.1),我们在--aggressive下使用与默认。

     

正如在有意义的提交中所指出的那样,将更深的深度设置为“积极的”默认设置是错误的,因此节省磁盘空间以牺牲运行时性能为代价,这通常与“侵略性的gc”想要。