“git gc”命令是否也会运行“git repack”?

7
我已关闭 git 烦人的自动打包功能(我猜大多数 git 用户在使用 git 时都会遇到“重新打包以达到最佳性能”的提示),并通过 cronjob 在夜间调用 “git gc”。
但是我不确定这样是否足够,是否需要在 "git gc"之前或之后运行 "git repack"。
"git repack" 和 "git gc" 的手册没有提到它们之间的任何连接,而 "git repack" 的手册实际上包含了这样一句话:
相反,下一个 git gc 调用将按照正常的到期规则修剪松散的不可访问对象。
这对我来说意味着 "git gc" 不足以完成所有的日常维护任务,还需要使用 "git repack"。那么,正确的操作是什么?应该使用哪些 git 维护命令?
3个回答

9

git repack命令只是重新打包对象。

git gc命令将它们重新打包并丢弃旧的不可访问的对象。

您可以通过执行类似于find .git/objects的命令来验证,在执行git gc之前和之后:在执行之前,您应该会看到所有新对象作为单独的文件。之后,应该只有一个大的打包文件。

详细信息还可以查看代码:在builtin/gc.c中,repack命令被准备执行


那么,问题的答案应该是:“不需要单独调用git-repack,git-gc已足够进行清理工作”,对吗? - Robby75
1
答案是“是的,git gc也会运行git repack”;) 因此,是的,git gc就足够了。 - michas

2
2022年更新(8年后)

git gc 确实会运行 repack
但不是通常的 repack。有一个新的 git repack --cruft,生成垃圾包

在 Git 2.37(2022年第三季度)中,引入了一种将无法访问的对象打包成 "垃圾包" 的机制,而不是将它们弹出为松散形式以便稍后回收。

使用以下命令激活该机制:

git config --global gc.cruftPacks true

查看 提交a613164, 提交5b92477, 提交ddee370, 提交72263ff, 提交4571324, 提交f9825d1, 提交a7d4938, 提交fb546d6, 提交2fb9040, 提交b757353, 提交fa23090, 提交2bd4427, 提交5dfaf49, 提交d9fef9d, 提交1c573cd, 提交94cd775, 提交3d89a8c (2022年5月20日) 由Taylor Blau (ttaylorr)完成。
(由Junio C Hamano -- gitster --合并于提交a50036d, 2022年6月3日)

文档/技术:添加cruft-packs.txt

签署者:Taylor Blau

创建一个技术文档来解释cruft包。
它包含了问题的简要概述、一些背景信息、实现细节以及几种未在此处考虑的替代方法。

technical/cruft-packs现在已经包含在其手册页面中:

Cruft packs

“Cruft packs”提供了一种替代Git传统机制的功能,即删除不可达对象。本文档介绍了Git修剪机制的概述,以及如何使用“Cruft packs”来完成相同的任务。

背景

为了从仓库中删除不可达对象,Git 提供了 git repack -Ad 命令(参见git repack)。引用文档:

[quote] [...] 先前包中的不可达对象变成未打包的松散对象,而不是留在旧包中。[...] 松散的不可达对象将根据正常到期规则在下一个 'git gc' 调用时进行修剪。

不可达对象不会立即被删除,因为这样做可能会与正在进行的推送竞争,该推送可能引用即将被删除的对象。相反,这些不可达对象被存储为松散对象,并保持这种状态,直到它们的年龄超过到期时间窗口,此时它们将被 git prune 删除。

Git 必须将这些不可达对象松散地存储,以便跟踪它们的每个对象修改时间。如果这些不可达对象被写入一个大包中,那么刷新该包(因为其中包含的对象已被重写)或创建一个新的不可达对象包将导致该包的修改时间得到更新,并且其中的对象永远不会离开到期时间窗口。
相反,为了跟踪各个对象的修改时间并避免所有杂质对象同时刷新的情况,对象被松散地存储。

当仓库包含许多未离开宽限期的不可达对象时,这可能会导致不良情况。
.git/objects 的碎片目录中有大量文件会导致仓库性能下降。
但是,如果不可达对象足够多,则可能会导致 inode 不足,从而降低整个系统的性能。
由于我们永远无法打包这些对象,因此这些仓库通常占用大量磁盘空间,因为我们只能对它们进行 zlib 压缩,但不能将它们存储在 delta 链中。

Cruft packs

“Cruft packs”通过在单个包中包含所有松散对象的同时,在单独的文件中包含每个对象的修改时间,消除了存储不可达对象的松散状态的需要。

当生成新的包时,通过 git repack --cruftgit pack-objects--cruft 选项来编写“Cruft packs”。请注意,git repack --cruft 是一个经典的全合并包,这意味着结果包中的所有内容都是可达的,而其他所有内容都是不可达的。
一旦编写完成,“--cruft” 选项指示 git repack 生成另一个包,其中只包含上一步未打包的对象(即打包所有不可达对象)。

该进程如下进行:

  1. 枚举每个对象,将任何 (a) 不包含在保留

    并且:

    builtin/gc.c:有条件地避免通过松散方式修剪对象

    签名作者:Taylor Blau

    将新的git repack --cruft模式从git gc中通过一个新的选择性标志公开。当像git gc --cruft这样调用时,git gc将避免将不可访问的对象作为松散对象爆炸,并创建一个杂物包和.mtimes文件。(链接1) (链接2) (链接3) (链接4) (链接5) (链接6)

    git config现在在其手册页中包含:

    gc.cruftPacks

    将不可访问的对象存储在一个废弃的包中(参见git repack),而不是作为散装对象。

    默认值为false

    git config现在在其手册页中包含:

    当运行 'git gc' 时,它将调用 'prune --expire 2.weeks.ago'(如果使用 gc.cruftPacks--cruft 进行垃圾包处理,则还会调用 'repack --cruft --cruft-expiration 2.weeks.ago')。可以通过此配置变量覆盖优雅期。值 "now" 可以用于禁用此优雅期并立即修剪不可达对象,或者可以使用 "never" 来禁止修剪。当 'git gc' 与另一个写入存储库的进程同时运行时,此功能有助于防止损坏;请参见 git gc 的 "NOTES" 部分。
    现在,'git gc' 的 手册页 中包括:
    “--cruft”选项:在删除无法访问的对象时,将它们单独打包成一个“cruft pack”,而不是将这些松散的对象作为松散对象存储。
    使用 Git 2.39(2022年第四季度),对于选择启用“feature.experimental”设置的用户,默认启用gc.cruftpacks
    参见提交记录c695592提交记录12253ab(2022年10月26日),作者为Emily Shaffer(nasamuffin
    (由Taylor Blau -- ttaylorr --提交记录bdd42e3中合并,于2022年11月8日)

    config: 让 feature.experimental 暗示 gc.cruftPacks=true

    签署者:Emily Shaffer
    签署者:Taylor Blau

    我们有兴趣探索是否应该将 gc.cruftPacks=true 变为默认值。

    为了确定这样做是否安全,让我们鼓励更多用户尝试它。

    已经设置 feature.experimental=true 的用户已经自愿尝试新的、可能会破坏配置的更改,因此让我们在这组用户中尝试这个新的默认值。

    git config 现在在其手册页面中包括:

    • gc.cruftPacks=true可以在垃圾回收期间减少不可达对象占用的磁盘空间,从而防止松散对象爆炸。

    示例

    git reset HEAD~2
    git -c gc.cruftPacks=true gc
    # or
    git gc --cruft
    
    find .git/objects/pack -name "*.mtimes" >mtimes
    sed -e 's/\.mtimes$/\.pack/g' mtimes >packs 
    while read pack
    do
      test_path_is_file "$pack" || return 1
    done <packs
    

    那里不应该产生任何pack文件,因为最后两个提交已经无法到达。
    在 Git 2.39(2022年第四季度),"git repack"(man) 学会将无用的对象移至存储库外的 pack 文件。(原文链接) 查看 提交 91badeb, 提交 c12cda4, 提交 eddad36, 提交 4e7b65b (2022年10月24日) 由 Taylor Blau (ttaylorr) 提交。
    (合并于 提交 ad90968,由 Taylor Blau -- ttaylorr -- 完成,日期为2022年11月18日) builtin/repack.c:实现--expire-to以存储修剪后的对象。

    签名:Taylor Blau

    When pruning objects with --cruft, git repack(man) offers some flexibility when selecting the set of which objects are pruned via the --cruft-expiration option.

    This is useful for expiring objects which are older than the grace period, making races where to-be-pruned objects become reachable and then ancestors of freshly pushed objects, leaving the repository in a corrupt state after pruning substantially less likely.

    But in practice, such races are impossible to avoid entirely, no matter how long the grace period is.
    To prevent this race, it is often advisable to temporarily put a repository into a read-only state.
    But in practice, this is not always practical, and so some middle ground would be nice.

    This patch introduces a new option, --expire-to, which teaches git repack to write an additional cruft pack containing just the objects which were pruned from the repository.
    The caller can specify a directory outside of the current repository as the destination for this second cruft pack.

    This makes it possible to prune objects from a repository, while still holding onto a supplemental copy of them outside of the original repository.
    Having this copy on-disk makes it substantially easier to recover objects when the aforementioned race is encountered.

    --expire-to is implemented in a somewhat convoluted manner, which is to take advantage of the fact that the first time write_cruft_pack() is called, it adds the name of the cruft pack to the names string list.
    That means the second time we call write_cruft_pack(), objects in the previously-written cruft pack will be excluded.

    As long as the caller ensures that no objects are expired during the second pass, this is sufficient to generate a cruft pack containing all objects which don't appear in any of the new packs written by git repack, including the cruft pack.
    In other words, all of the objects which are about to be pruned from the repository.

    It is important to note that the destination in --expire-to does not necessarily need to be a Git repository (though it can be) Notably, the expired packs do not contain all ancestors of expired objects.
    So if the source repository contains something like:

    <unreachable>
                 /
        C1 --- C2
          \
    ads/master
    

    where C2 is unreachable, but has a parent (C1) which is reachable, and C2 would be pruned, then the expiry pack will contain only C2, not C1.

    git repack现在在其手册页面中包含:

    --expire-to=<dir>

    将包含修剪对象(如果有)的垃圾包写入目录<dir>。此选项可用于将任何修剪对象的副本保留在单独的目录中作为备份。仅与--cruft -d一起使用时有用。

    示例:

    git repack -d \
                --cruft --cruft-expiration=5.minutes.ago \
                --expire-to="myRepo/objects/pack/pack
    

0

在 Git 2.43(2023 年第四季度)中,"git repack"(man) 学会了 --max-cruft-size,以防止废弃包无限增长。

由于 git gc 运行 git repack --cruft,这将帮助 git gc 避免留下大型废弃物。

查看提交 c1b754d(2023年10月7日)由Jeff King(peff提交。
查看提交 3c1e2c2(2023年10月3日),以及提交 37dc6d8提交 b5b1f4c提交 78de1c6(2023年10月2日)由Taylor Blau(ttaylorr提交。
(由Junio C Hamano -- gitster --提交 79861ba中合并,2023年10月18日) builtin/repack.c:实现对--max-cruft-size的支持 签名:Taylor Blau Cruft packs是一种替代机制,用于存储一组无法访问的对象,其修改时间足够新,以避免被从存储库中删除。当Cruft packs首次引入时(参见b757353(“builtin/pack-objects.c: --cruft without expiration”,2022-05-20,Git v2.37.0-rc0 -- merge listed in batch #7)和a7d4938(“builtin/pack-objects.c: --cruft with expiration”,2022-05-20,Git v2.37.0-rc0 -- merge listed in batch #7),推荐的工作流程包括:

  • 定期重新打包,可以通过打包存储库中的任何松散对象(通过git repack -d(man))或生成一系列几何级数的packs(通过git repack --geometric=<d> -d(man))来实现。
  • 偶尔将存储库拆分为两个packs,一个用于存储无法访问的对象的cruft pack,另一个用于存储可访问的对象的非cruft pack。
请参阅我之前的回答,了解关于Crufts的详细信息。
(以上内容之外)存储库可以选择定期修剪一些已经超过优雅期限的不可达对象,通过生成一个带有--cruft-expiration=<approxidate>的包来实现。
这样可以使存储库平均维护较少的包,并将不可达对象一起隔离在一个垃圾包中,避免了在它们过期时将不可达对象保持为松散状态的问题(有关更多详细信息,请参见3d89a8c(“Documentation/technical: add cruft-packs.txt”,2022-05-20,Git v2.37.0-rc0 -- merge listed in batch #7))。
这一切都有效,但是当频繁重新打包具有许多不可达对象的存储库时,从I/O角度来看可能会很昂贵。
当这些不可达对象很少(如果有的话)被修剪时,这个问题会加剧。
由于上述方案中最多只有一个垃圾包,每次更新垃圾包时都必须从头开始重写。
由于大部分包被重复使用,从CPU角度来看,这是一个相对廉价的操作,但从I/O角度来看,这是一个非常昂贵的操作,因为我们最终会重写基本相同的包(加上自上次生成垃圾包以来进入存储库的任何新的不可达对象)。
当时,我们决定不实现对多个垃圾包的更强大支持。
这个补丁实现了我们缺少的支持。
引入一个新选项--max-cruft-size,允许存储库累积垃圾包,直到达到给定的大小,此后可以累积新一代的垃圾包,直到达到最大大小,依此类推。
生成新的垃圾包的过程如下:
- 按照包大小的升序对任何现有垃圾包的列表进行排序。 - 从列表的开头开始,将垃圾包分组,直到累积大小小于指定的最大包大小。 - 将这些垃圾包中的对象与任何其他进入存储库的不可达对象一起组合成一个新的垃圾包。
一旦垃圾包的大小超过通过--max-cruft-size指定的大小,该包就被冻结。
这将将I/O的翻转限制为与--max-cruft-size选项指定的值的二次函数,而不是与总不可达对象数量的二次函数相同。
在修剪不可达对象时,我们绕过了将小垃圾包组合在一起的新代码路径,而是从头开始,将适当的--max-pack-size传递给pack-objects,让它负责保持生成的垃圾包集的正确大小。
这可能看起来像是更多的I/O翻转,但实际上并不那么糟糕。
我们可以修剪那些所有或大多数对象被删除的旧垃圾包,然后生成一个只包含剩余对象的新垃圾包。
但是这种额外的复杂性对我们来说带来的好处相对较少,因为大多数对象最终都会被修剪,所以I/O翻转是可控的。

git config现在在其man page中包含以下内容:

gc.maxCruftSize

限制重新打包时新垃圾包的大小。当与--max-cruft-size一起指定时,命令行选项优先。请参阅git repack--max-cruft-size选项。

git gc现在在其man page中包含以下内容:

--max-cruft-size=<n>

当将无法访问的对象打包到垃圾包时,限制新垃圾包的大小最多为<n>字节。覆盖任何通过gc.maxCruftSize配置指定的值。有关更多信息,请参见git repack--max-cruft-size选项。

git repack现在在其手册页中包含了这个功能。

--max-cruft-size=<n>

在创建新的包之前,将无用对象重新打包成大小为<n>字节的包。只要存在足够小于<n>的无用包,重新打包将会创建一个新的无用包,其中包含来自任何组合无用包以及任何新的无法访问的对象。大于<n>字节的无用包将不会被修改。当新的无用包大于<n>字节时,它将被拆分为多个包,所有这些包的大小都保证不超过<n>字节。仅与--cruft -d一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接