git gc
和有什么区别git repack -ad; git prune
吗?
如果是,将采取哪些额外步骤git gc
(反之亦然)?
在空间优化或安全方面,哪个更好用?
3 回答
git gc
和有什么区别git repack -ad; git prune
吗?
不同之处在于,默认情况下git gc
,对于需要哪些内务处理任务非常保守。例如,git repack
除非存储库中松散对象的数量高于某个阈值(可通过gc.auto
变量配置),否则它不会运行。此外,git gc
将运行更多的任务,而不仅仅是git repack
和git prune
.
如果是,将采取哪些额外步骤
git gc
(反之亦然)?
根据文档,git gc
运行:
git-prune
git-reflog
git-repack
git-rerere
更具体地说,通过查看(lines 338-343) 1的源代码,gc.c
我们可以看到它最多调用以下命令:
pack-refs --all --prune
reflog expire --all
repack -d -l
prune --expire
worktree prune --expire
rerere gc
根据包的数量(第 121-126 行),它可以repack
使用-A
选项运行(第 203-212行):
* If there are too many loose objects, but not too many * packs, we run "repack -d -l". If there are too many packs, * we run "repack -A -d -l". Otherwise we tell the caller * there is no need. if (too_many_packs()) add_repack_all_option(); else if (!too_many_loose_objects()) return 0;
请注意函数的第 211-212 行need_for_gc
,如果存储库中没有足够的松散对象,gc
则根本不会运行。
文档中进一步阐明了这一点:
如果存储库中有太多松散的对象或太多的包,则需要进行内务管理。如果松散对象的数量超过
gc.auto
配置变量的值,则所有松散对象都使用git repack -d -l
. 将 的值设置gc.auto
为0
禁用松散对象的自动打包。如果包的数量超过 的值,则使用 的选项
gc.autoPackLimit
将现有包(标有文件的包除外.keep
)合并为一个包。-A
git repack
如您所见,git gc
努力根据存储库的状态做正确的事情。
在空间优化或安全方面,哪个更好用?
一般来说,最好git gc --auto
简单地运行,因为它将做最少的工作来保持存储库的良好状态——安全且不会浪费太多资源。
但是,请记住,垃圾收集可能已经在某些命令之后自动触发,除非通过将gc.auto
配置变量设置为禁用此行为0
。
从文档中:
--auto
使用此选项,git gc
检查是否需要任何内务处理;如果没有,它会退出而不执行任何工作。一些 git 命令git gc --auto
在执行可能创建许多松散对象的操作后运行。
因此,对于大多数存储库,您不需要git gc
经常显式运行所有内容,因为它已经为您处理好了。
1. 截至a0a1831
2016-08-08 提交。
git help gc
包含一些提示...
可选配置变量 gc.rerereresolved 指示您之前解决的冲突合并记录保留多长时间。
可选配置变量 gc.rerereunresolved 指示您尚未解决的冲突合并记录保留多长时间。
我相信如果你只做这些就不会做git repack -ad; git prune
。
请注意,git prune
由 运行git gc
,前者随着 Git 2.22(2019 年第二季度)的发展而发展
" git prune
" 已被教导在可能的情况下利用可达性位图。
请参阅Jeff King ( )的提交 cc80c95、提交 c2bf473、提交 fde67d6、提交 d55a30b(2019 年 2 月 14 日) 。(由Junio C Hamano 合并 -- --在提交 f7213a3中,2019 年 3 月 7 日)peff
gitster
prune
:使用位图进行可达性遍历
修剪通常必须遍历整个提交图才能查看哪些对象是可访问的。
这正是可达性位图要解决的问题,所以让我们使用它们(当然,如果它们可用的话)。
请参阅此处的可达性位图。
以下是 git.git 的时间安排:
Test HEAD^ HEAD ------------------------------------------------------------------------ 5304.6: prune with bitmaps 3.65(3.56+0.09) 1.01(0.92+0.08) -72.3%
在 linux.git 上:
Test HEAD^ HEAD -------------------------------------------------------------------------- 5304.6: prune with bitmaps 35.05(34.79+0.23) 3.00(2.78+0.21) -91.4%
测试显示了一个非常理想的情况,因为我们刚刚重新打包并且应该使用我们的位图很好地覆盖所有参考。
但这实际上是非常现实的:通常 prunegc
在重新打包后通过 " " 运行。实现注意事项:更改实际上是在 中
reachable.c
,因此它也将通过“”提高可达性遍历reflog expire --stale-fix
。
但是,这些并没有定期执行(正常的“git gc
”不使用--stale-fix
),因此它们并不值得衡量。由于从调用者的角度来看,位图的使用是完全透明的,因此该调用者回归的可能性很小。
和:
请参阅Jeff King ( ) 的提交 fe6f2b0(2019 年 4 月 18 日)。(由Junio C Hamano 合并 -- --在d1311be 提交中,2019 年 5 月 8 日)peff
gitster
prune:懒惰地执行可达性遍历
“”的一般策略
git prune
是进行一次完全可达性步行,然后对于每个松散的对象,看看我们是否在步行中找到了它。
但是如果我们没有任何松散的物体,我们一开始就不需要进行昂贵的步行。这个补丁推迟了这个步行,直到我们第一次需要看到它的结果。
请注意,这实际上是更一般优化的特定情况,即我们只能遍历足够远以找到正在考虑的对象(即,当我们找到它时停止遍历,然后在询问下一个对象时再次选择, ETC)。
在某些情况下,这可以使我们免于步行。但是使用我们的遍历代码实际上有点棘手,如果您甚至有一个无法访问的对象(您通常会这样做,如果在运行 git-repack 后实际上留下了任何对象),那么您无论如何都需要进行完整的遍历.因此,在实践中,这种完整步行的延迟加载捕获了一个简单但常见的情况(即,您刚刚通过 重新打包
git-gc
,并且没有什么是无法到达的)。perf 脚本相当做作,但它确实展示了改进:
Test HEAD^ HEAD ------------------------------------------------------------------------- 5304.4: prune with no objects 3.66(3.60+0.05) 0.00(0.00+0.00) -100.0%
如果我们不小心退回了这个优化,我们会告诉我们。
还要注意,我们需要特别注意
prune_shallow()
,它依赖于我们已经执行了遍历。
因此,这种优化只能用于非浅层存储库。由于这很容易出错并且现有测试没有涵盖,因此让我们添加一个额外的测试来t5304
明确涵盖这种情况。
prune
:使用位图进行可达性遍历
修剪通常必须遍历整个提交图才能查看哪些对象是可访问的。
这正是可达性位图要解决的问题,所以让我们使用它们(当然,如果它们可用的话)。以下是时间
git.git
:Test HEAD^ HEAD ------------------------------------------------------------------------ 5304.6: prune with bitmaps 3.65(3.56+0.09) 1.01(0.92+0.08) -72.3%
并且在linux.git
:
Test HEAD^ HEAD -------------------------------------------------------------------------- 5304.6: prune with bitmaps 35.05(34.79+0.23) 3.00(2.78+0.21) -91.4%
测试显示了一个非常理想的情况,因为我们刚刚重新打包并且应该使用我们的位图很好地覆盖所有参考。
但这实际上是非常现实的:通常 prunegc
在重新打包后通过 " " 运行。关于实现的几点说明:
更改实际上是在 中
reachable.c
,因此它也可以通过“”来提高可达性遍历reflog expire --stale-fix
。
但是,这些并没有定期执行(正常的“git gc
”不使用--stale-fix
),因此它们并不值得衡量。
由于从调用者的角度来看,位图的使用是完全透明的,因此该调用者回归的可能性很小。位图的情况实际上可以在不创建“
struct object
”的情况下消失,而调用者只需在位图结果中查找每个对象 id。但是,这将是运行时的边际改进,并且会使调用者更加复杂。
他们必须分别处理位图和非位图情况,在 的情况下git-prune
,我们还必须调整prune_shallow()
,这取决于我们的SEEN
标志。因为我们确实创建了真实的对象结构,所以我们通过一些扭曲来创建正确类型的结构。
这不是绝对必要的(lookup_unknown_object()
就足够了),但是使用正确的类型会更有效率,因为我们已经知道它们。
当可达性位图生效时(自 Git 2.22, 2019 起),“不要丢失最近创建的对象和可从它们访问的对象”保护我们免受竞争的安全性被错误地禁用:这已在 Git 2.32 中得到纠正( 2021 年第二季度)。
请参阅Jeff King ( ) 的提交 2ba582b和提交 1e951c6(2021 年 4 月 28 日)。(由Junio C Hamano 合并 -- --在提交 6e08cbd中,2021 年 5 月 7 日)peff
gitster
prune
: 使用位图保存可访问的最近对象报告人:David Emett
签字人:Jeff King
我们将剪枝过期时间传递给
mark_reachable_objects()
,它不仅会遍历可达对象,还会考虑任何最近的对象作为可达性的提示;有关详细信息,请参见d3038d2(“prune
:保持对象可从最近的对象访问”,2014-10-15,Git v2.2.0-rc0 --合并)。但是,这与fde67d6中添加的位图代码路径交互不良(“
prune
:使用位图进行可达性遍历”,2019-02-13,Git v2.22.0-rc0 --批次 #2中列出的合并)。 如果我们点击位图优化路径,我们会立即返回以避免常规遍历,不小心跳过了“也遍历最近”的代码。相反,我们应该为位图与常规遍历做一个 if-else,然后在任何一种情况下都跟进“最近的”遍历。
这重用"rev_info"
了位图,然后是常规遍历,但这应该可以正常工作(位图代码以通常的方式清除挂起的数组,就像常规遍历一样)。