重打包存储库对大型二进制文件有用吗?

3
我正在尝试将Perforce的大量历史记录转换为Git,其中一个文件夹(现在是Git分支)包含大量的大型二进制文件。我的问题是,在运行git gc --aggressive时内存不足。
我的主要问题是重新打包存储库是否可能对大型二进制文件产生任何有意义的影响。将它们再压缩20%会很好。0.2%不值得我付出努力。如果不行,我会按照这里建议跳过它们。
背景是,我成功地使用git p4创建了我满意的存储库状态,但这在幕后使用git fast-import,因此我想在正式使用之前优化存储库,并且实际上任何提交都会自动触发缓慢的gc --auto。它目前处于裸状态下的约35GB。
所讨论的二进制文件在概念上似乎是嵌入式设备中使用的供应商固件。我认为大约有25个大小在400-700MB之间,还有大约几百个大小在20-50MB之间。它们可能是磁盘映像,但我不确定。随着时间的推移,存在各种版本和文件类型,我经常看到.zip、.tgz和.simg文件。因此,我预计原始代码会有重叠,但我不确定实际文件在这一点上看起来有多相似,因为我相信这些格式已经被压缩了,对吗?
这些二进制文件包含在一个(旧的)分支中,将极少被使用(甚至可以质疑是否需要版本控制,但超出范围)。当然,该分支的性能不需要很好。但我希望仓库的其余部分是合理的。
其他关于最佳打包或内存管理的建议欢迎提出。我承认我并不真正理解链接问题中讨论的各种git选项。我也不太明白--window--depth标志在git repack中的作用。但主要问题是重新打包二进制文件本身是否有任何意义。

Git 2.20(2018年第4季度)应该优化包文件,使代码库更加健壮。请参见下面的我的答案 - VonC
请注意,Git 2.38(2022年第三季度)中新增了一个设置git -c push.useBitmaps=false push,用于禁用git push的打包功能。 - VonC
4个回答

4
我的主要问题是,重新打包存储库是否会对大型二进制文件产生任何有意义的影响。
这取决于它们的内容。对于您特别概述的文件:
我经常看到.zip、tgz和.simg文件。
Zip文件和tgz(gzipped tar archive)文件已经被压缩,并且具有可怕的(即高)香农熵值 - 对于Git来说非常糟糕 - 不会相互压缩。.simg文件可能是(我必须猜测)Singularity磁盘映像文件; 我不知道它们是否以及如何被压缩,但我会假设它们被压缩了。(一个简单的测试是将其提供给压缩器,例如gzip,并查看它是否缩小。)
因此,我预计原始代码会有重叠部分,但我不确定实际文件在这一点上看起来有多相似,因为我认为这些格式已经被压缩了,对吧?
确切地说,以未压缩的形式在Git中存储它们会产生更大的压缩效果,这有点矛盾。(但打包可能需要大量内存。)
如果[这可能是徒劳的],我会按建议这里跳过它们。
这将是我在这里的第一冲动。 :-)
我承认我不太了解链接问题中讨论的各个git选项。我也不真正理解--window--depth标志在git repack中所做的事情。
各种限制很令人困惑(也很多)。还要注意的是,它们在克隆时不会被复制,因为它们处于.git/config中,这不是一个提交的文件,因此新的克隆不会选择它们。 .gitattributes文件会在克隆时被复制,新的克隆将继续避免打包不可打包的文件,因此这是更好的方法。
(如果您想深入了解细节,可以在Git技术文档中找到一些内容。这并没有精确讨论窗口大小的问题,但与Git在选择可能相互压缩的对象数据时使用多少内存来内存映射对象数据有关。有两个:一个用于单个包文件上的每个mmap,另一个用于所有包文件上的总聚合mmap。您链接中未提到的是:core.deltaBaseCacheLimit,这是用于保存增量基数的内存数量——但要理解这一点,您需要掌握增量压缩和增量链1,并阅读同样的技术文档。请注意,Git将默认不尝试打包任何文件对象,其大小超过core.bigFileThreshold。各种pack.*控件有点更复杂:如果可能的话,打包是多线程完成的,每个线程可以使用大量内存。限制线程数会限制总内存使用量:如果一个线程将使用256 MB,则8个线程可能会使用8 * 256 = 2048 MB或2 GB。位图主要加速从繁忙服务器获取。)

1它们并不那么复杂:当一个对象说“取XYZ对象并应用这些更改”时,就会发生Delta链,但是XYZ对象本身会说“取PreXYZ对象并应用这些更改”。PreXYZ对象还可以拿另一个对象,以此类推。Delta基础是此列表底部的对象。


谢谢!这正是我担心的...哦,好吧,至少这次运行得更快了。那是一篇非常有趣的文档阅读。我肯定不能说我完全理解了它,但我大致明白了,但不足以想要篡改那些值。但是排除大文件也能达到效果。跟进:有没有简单的方法知道哪些二进制类型是可压缩/低熵的? - ojchase
1
最佳答案(不是我写的,请参见此处中的方程式以获取详细信息)在这里。如果您不想编写自己的代码,那么一般来说,如果将低熵文件输入压缩器,则应大幅缩小文件大小,而如果将高熵文件输入其中,则文件大小可能不会缩小,甚至可能变得更大。 - torek

1

欢迎提供有关最佳打包或内存管理的其他建议。

Git 2.20 (2018年第4季度) 将会有一个优化:当存储库中有太多的pack文件时(不建议这样做),查找这些文件中的对象需要查询许多pack .idx 文件;引入了一种新机制,可以将所有这些.idx文件合并成一个单独的文件

请参见提交 6a22d52, 提交 e9ab2ed, 提交 454ea2e, 提交 0bff526, 提交 29e2016, 提交 fe86c3b, 提交 c39b02a, 提交 2cf489a, 提交 6d68e6a (2018年8月20日), 提交 ceab693 (2018年7月12日) 由Derrick Stolee (derrickstolee)完成。
(在提交49f210f中由Junio C Hamano -- gitster --合并, 2018年9月17日)

pack-objects:考虑多重包索引中的包

在运行'git pack-objects --local'时,我们希望避免打包存在于备用库中的对象。
目前,我们使用packed_git_mru列表检查这些对象,该列表排除了被多重包索引覆盖的包文件。

有一个新设置:

core.multiPackIndex::

Use the multi-pack-index file to track multiple packfiles using a single index.

这里解释了multi-pack index的内容,而且在Documentation/technical/multi-pack-index.txt中也有说明:

Multi-Pack-Index (MIDX) 设计笔记

Git 对象目录包含一个名为 'pack' 的目录,其中包含:

  • 打包文件(后缀为 ".pack")和
  • 打包索引文件(后缀为 ".idx")。

打包索引文件提供了一种查找对象并导航到其在打包文件中的偏移量的方式,但这些必须与打包文件成对出现
这种配对取决于文件名,因为打包索引文件仅在后缀上与其打包文件不同。

虽然打包索引文件为每个打包文件提供了快速查找,但是随着打包文件数量的增加,性能会降低,因为缩写需要检查每个打包文件,并且我们更有可能在最近使用的打包文件中错过。

对于一些大型存储库,由于存储空间或过多的重新打包时间,重新打包成单个打包文件是不可行的。

多打包索引(简称 MIDX)将对象及其在多个打包文件中的偏移量列表存储起来。
它包含:

  • 打包文件名称列表。
  • 已排序的对象 ID 列表。
  • 第 i 个对象 ID 的元数据列表,其中包括:
  • 引用第 j 个打包文件的值 j。
  • 对象在第 j 个打包文件中的偏移量。
  • 如果需要大偏移量,则使用类似于版本 2 打包索引的另一个大偏移量列表。

因此,我们可以为任意数量的打包文件提供 O(log N) 的查找时间。


Git 2.23 (2019年第三季度) 新增了两个命令,"git multi-pack-index" 学习了 "expire" 和 "repack" 子命令。

请查看 提交 3612c23 (2019年7月1日),以及 提交 b526d8c, 提交 10bfa3f, 提交 d274331, 提交 ce1e4a1, 提交 2af890b, 提交 19575c7, 提交 d01bf2e, 提交 dba6175, 提交 cff9711, 提交 81efa16, 提交 8434e85,作者为 Derrick Stolee (derrickstolee)
协助者为:Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- 合并于 提交 4308d81,2019年7月19日)

multi-pack-index:为实现'expire'子命令做准备/实现

多包索引跟踪一组打包文件中的对象
每个对象只有一个副本被索引,使用打包文件的修改时间来确定优先级。
可能存在一个打包文件没有引用的对象,因为所有对象在新的打包文件中都有重复。

引入一个新的'expire'子命令到多包索引内置函数中。
这个子命令将删除这些未使用的打包文件,并重新编写多包索引以不再引用那些文件

'git multi-pack-index expire'子命令:

  • 查看现有的多包索引,
  • 计算每个打包文件中引用的对象数量,
  • 删除没有被引用的打包文件,以及
  • 重新编写多包索引以不再引用那些打包文件。

文档:

expire:

删除由MIDX文件跟踪但未被MIDX引用的打包文件。之后重写MIDX文件以删除对这些打包文件的所有引用。

并且:

multi-pack-index: 准备/实现 'repack' 子命令

在多包索引非常有用的环境中,由于存在许多包文件且无法将对象存储库重新打包为单个包文件。但是,这些包文件很可能都比较小,可以轻松地将它们重新打包成一个稍大的包文件。
还可能需要确保对象存储库高度可用,并且重新打包操作不会中断并发的git命令。
引入'repack'子命令到'git multi-pack-index'中,并添加'--batch-size'选项。
该子命令将检查多包索引中引用的包文件,如果其大小小于批处理大小,则收集包含这些包文件的列表,直到这些包文件的大小总和大于批处理大小。
然后,将创建一个新的包文件,其中包含多包索引引用的那些包文件中的对象。
由于压缩和包文件中可能存在其他包文件中具有重复副本的对象,因此生成的包实际上可能比批处理大小要小。
'git multi-pack-index repack'命令可以采用零批处理大小,从而创建一个包含多包索引中所有对象的新包文件。
使用零批处理大小非常类似于标准的'git repack'命令,只是我们不删除旧包文件,而是依靠新的多包索引防止新进程读取旧包文件。
这不会干扰当前基于旧多包索引读取旧包的其他Git进程。
第一个'repack'命令将创建一个新的包文件,之后的'expire'命令将删除旧的包文件,因为它们不再包含多包索引中引用的任何对象。

文档

repack:

Create a new pack-file containing objects in small pack-files referenced by the multi-pack-index.
If the size given by the --batch-size=<size> argument is zero, then create a pack containing all objects referenced by the multi-pack-index.

For a non-zero batch size:

  • select the pack-files by examining packs from oldest-to-newest,
  • computing the "expected size" by counting the number of objects in the pack referenced by the multi-pack-index,
  • then divide by the total number of objects in the pack and
  • multiply by the pack size.

We select packs with expected size below the batch size until the set of packs have total expected size at least the batch size.

  • If the total size does not reach the batch size, then do nothing.
  • If a new pack-file is created, rewrite the multi-pack-index to reference the new pack-file.
    A later run of 'git multi-pack-index expire' will delete the pack-files that were part of this batch.

使用Git 2.25(2020年第一季度),生成多包索引的代码学会了显示(或不显示)进度指示器。这对于大型二进制文件非常有用。

请查看 提交 680cba2, 提交 64d80e7, 提交 ad60096, 提交 8dc18f8, 提交 840cef0, 提交 efbc3ae (2019年10月21日) 由 William Baker (wjbaker101) 提交。
(于 2019年11月10日 由 Junio C Hamano -- gitster -- 合并至 提交 8f1119b)

multi-pack-index: 添加 [--[no-]progress] 选项。

Signed-off-by: William Baker

添加--[no-]progress选项到 git multi-pack-index
当需要通过multi-pack-index显示进度时,将MIDX_PROGRESS标志传递给子命令函数。
进度功能已经添加到144d703的“verify”中(“multi-pack-index: report progress during 'verify'”,2018年9月13日,Git v2.20.0-rc0 -- merge listed in batch #3),但是一些子命令没有更新以显示进度,并且忽略了退出的能力。
不要忘记阅读Documentation/technical/pack-format.txt,其中包括多包索引(MIDX)文件格式描述。 从Git 2.25.1(2020年2月)开始,有文档修复。
请参见commit eb31044(2020年2月7日),作者为Johannes Berg (berghallen)(由Junio C Hamano -- gitster --commit 0410c2b中合并,于2020年2月12日)

pack-format: 修正多包索引说明

签名:Johannes Berg
确认:Derrick Stolee

多包索引的描述存在一个小错误,如果所有偏移量都是< 2^32,那么就不会有LOFF块,而不仅仅是当它们都是< 2^31时才没有LOFF块(因为只有在实际需要“LOFF转义”时才需要最高位)。

请修正此问题,并澄清在这种情况下,只能在OOFF块中存储最多2^31-1个偏移量。

pack-format文档现已包含:

2:打包内偏移量。

如果所有偏移量小于2^32,那么大偏移块将不存在,并且偏移量将存储为IDX v1中的方式。
如果存在至少一个大于2^32-1的偏移值,则必须存在大偏移块,并且偏移大于2^31-1必须存储在其中。
如果存在大偏移块并且第31位为开,则移除该位可显示包含此对象的8字节偏移量的大偏移中行。


在Git 2.27之前(2020年第二季度),当读入记录没有对象的midx(多包索引)时,某些代码路径尝试通过从0到“num_objects-1”的循环来访问它,由于整数算术的包装,这使得数组访问越界成为无意义的操作。

该代码已被更正以拒绝此类midx文件。

参见commit 796d61c(由Damien Robert (damiens-robert)于2020年3月28日提交)
(由Junio C Hamano -- gitster --于2020年4月22日合并至commit 8777ec1

midx.c: 修复一个整数下溢问题

Signed-off-by: Damien Robert

当验证一个包含0个对象的midx索引时,m->num_objects - 1会发生下溢并回绕到4294967295

通过检查midx是否至少包含一个oid,并且在没有packfile时不写入任何midx来解决此问题。

更新测试以检查git multi-pack-index write在没有对象时不会写入midx,另一个测试检查git multi-pack-index verify在验证一个没有对象的midx时会发出警告。


在Git 2.27(2020年第二季度), "git multi-pack-index repack" 已经学会了尊重一些 repack.* 配置变量。

请参见 提交3ce4ca0(2020年5月10日),提交者为Derrick Stolee(derrickstolee
请参见 提交e11d86d(2020年5月10日),提交者为Son Luong Ngoc(sluongng
(由Junio C Hamano -- gitster --合并于提交6baba94,2020年5月14日)

midx: 教授 "git multi-pack-index repack" 尊重 "git repack" 配置

Signed-off-by: Son Luong Ngoc

当 "git multi-pack-index" 命令的 "repack" 子命令创建新的包文件时,它不会调用 "git repack" 命令,而是直接调用 "git pack-objects" 命令,并且 "git repack" 命令所用的配置变量,如 "repack.usedaeltabaseoffset",将被忽略。

我们在 "git multi-index-pack" 中检查 "git repack" 使用的配置变量,并将相应选项传递给底层的 "git pack-objects"。

请注意,repack.writeBitmaps 配置将被忽略,因为打包位图功能只有在单个包文件中才有用。

multi-pack-index: 尊重 repack.packKeptObjects=false

报告者: Son Luong Ngoc
签署者: Derrick Stolee

在 "git multi-pack-index repack" 命令中选择一批要重新打包的打包文件时,Git 应该尊重 repack.packKeptObjects 配置选项。
当为 false 时,该选项表示与 ".keep" 文件相关联的打包文件不应该被重新打包。
此配置值默认为 "false"。

有两种情况可以选择一批对象。
第一种情况是输入批处理大小为零,这指定“重新打包所有内容”。
第二种情况是使用贪婪选择标准选择打包文件的非零批处理大小。
这两种情况都已更新和测试过。


在 Git 2.29 (2020年第四季度) 中, "git multi-pack-index repack"(man) 命令的 "--batch-size" 选项现在用于指定将非常小的 packfiles 收集到一个 packfile 中,直到总大小大致超过它。请保留 HTML 标签。

查看 提交 1eb22c7 (2020年8月11日) by Derrick Stolee (derrickstolee)
(由 Junio C Hamano -- gitster -- 合并于 提交 9e8c754,2020年8月24日)

multi-pack-index:重新打包小于--batch-size的批次

签署者:Derrick Stolee
审核者:Taylor Blau

“--batch-size=”选项是 'git multi-pack-index repack(man) ' 命令中用来限制重打包操作量的。对于大型代码库,该命令应该会重新打包一些小的文件包,而不会动大的文件包。通常情况下,代码库会有一个来自 'git clone(man) ' 操作的大文件包和一些增量 'git fetch(man) ' 操作的小文件包。

'--batch-size' 的问题在于,如果生成的文件包预期大小太小,它也会阻止重打包操作。

这原本是为了避免频繁更改小文件包,但在代码库规模“中等”的情况下,它通常会导致混淆。
也就是说,不像Windows OS代码库那么庞大,但也不小到这种增量重打包不重要的程度。

这里提出的解决方案是,收集文件包以进行重打包,如果它们的预期大小小于批处理大小参数,则收集直到总预期大小超过批处理大小或考虑所有文件包为止。
如果有至少两个文件包,则将它们合并为一个新的文件包,其大小不应该比批处理大小大太多。

这种新策略应该能够在这些“中等”规模的代码库中保持文件包数量较少。对于更改的担忧可能并不重要,因为真正控制频繁程度的是重打包命令的执行频率。

git multi-pack-index现在在其手册页面中包括以下内容:

我们选择预期大小低于批量大小的数据包,直到数据包集合的总预期大小至少为批量大小,或者考虑所有数据包文件。
如果只选择一个数据包文件,则不执行任何操作。
如果创建了新的数据包文件,则重写多重包索引以引用新的数据包文件。

稍后运行'git multi-pack-index expire'将删除此批次的数据包文件。


当通过 "git repack"(man) 删除packfile时,multi-pack-index会被清除;Git 2.29(2020年第四季度)通过首先检查midx是否实际上引用了不存在的pack来更加谨慎地教授代码。请注意保留html标签。

请参见提交 59552fb(2020年8月28日)和提交 e08f7bb(2020年8月25日),作者为Taylor Blau(ttaylorr
(由Junio C Hamano -- gitster --提交 a31677d中合并,2020年9月9日)

builtin/repack.c: 仅在必要时使MIDX失效

协助者:Derrick Stolee
签署者:Taylor Blau

525e18c04b ("midx: clear midx on repack", 2018-07-12, Git v2.20.0-rc0 -- merge listed in batch #1) 中,'git repack(man)' 学会了在添加或删除对象存储中的包时删除多重包索引文件。但这种机制有些过于迫切,因为只有当 'git repack(man)' 删除 MIDX 引用的包时才需要删除 MIDX。
在 MIDX 之外添加一个包不需要使 MIDX 失效,同样地,对于 MIDX 不知道的包进行删除也不需要失效 MIDX。
通过加载 MIDX 并检查即将删除的包是否为 MIDX 所知,教授 'git repack(man)' 检查此问题。
添加了一个新测试以显示当已知的两个包都标记为 .keep 时 MIDX 不受影响,但是删除了两个未知的包并将其合并成一个新包。

在 Git 2.32(2021 年第二季度)中,存在一个磁盘上的反向索引,可以将对象在多个打包文件中的位置映射回其对象名称。

请参见 提交 3007752 (2021年3月30日),作者为 Jeff King (peff)
请参见 提交 38ff7ca, 提交 a587b5a, 提交 f894081, 提交 b25fd24, 提交 62f2c1b, 提交 9f19161, 提交 7240cc4, 提交 9218c6a, 提交 86d174b, 提交 cd57bc4, 提交 690eb05, 提交 60ca947, 提交 b25b727, 提交 cf1f538, 提交 f7c4d63 (2021年3月30日),作者为 Taylor Blau (ttaylorr)
请参见 提交 1187556 (2021年2月24日),作者为 Junio C Hamano (gitster)
(由 Junio C Hamano -- gitster --提交 e6b971f 中合并,于2021年4月8日)

文档/技术:描述多包反向索引

共同作者:Jeff King
已签署:Jeff King
已签署:Taylor Blau

在实现多包位图之前,需要先了解和描述多包反向索引的格式和顺序。

技术/打包格式现在在其手册页面中包含了此信息:

multi-pack-index reverse indexes

Similar to the pack-based reverse index, the multi-pack index can also be used to generate a reverse index.

Instead of mapping between offset, pack-, and index position, this reverse index maps between an object's position within the MIDX, and that object's position within a pseudo-pack that the MIDX describes (i.e., the ith entry of the multi-pack reverse index holds the MIDX position of ith object in pseudo-pack order).

To clarify the difference between these orderings, consider a multi-pack reachability bitmap (which does not yet exist, but is what we are building towards here). Each bit needs to correspond to an object in the MIDX, and so we need an efficient mapping from bit position to MIDX position.

One solution is to let bits occupy the same position in the oid-sorted index stored by the MIDX. But because oids are effectively random, their resulting reachability bitmaps would have no locality, and thus compress poorly. (This is the reason that single-pack bitmaps use the pack ordering, and not the .idx ordering, for the same purpose.)

So we'd like to define an ordering for the whole MIDX based around pack ordering, which has far better locality (and thus compresses more efficiently). We can think of a pseudo-pack created by the concatenation of all of the packs in the MIDX. E.g., if we had a MIDX with three packs (a, b, c), with 10, 15, and 20 objects respectively, we can imagine an ordering of the objects like:

|a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|

where the ordering of the packs is defined by the MIDX's pack list, and then the ordering of objects within each pack is the same as the order in the actual packfile. Objects from the MIDX are ordered as follows to string together the pseudo-pack. Let pack(o) return the pack from which o was selected by the MIDX, and define an ordering of packs based on their numeric ID (as stored by the MIDX). Let offset(o) return the object offset of o within pack(o). Then, compare o1 and o2 as follows:

  • If one of pack(o1) and pack(o2) is preferred and the other is not, then the preferred one sorts first.

(This is a detail that allows the MIDX bitmap to determine which pack should be used by the pack-reuse mechanism, since it can ask the MIDX for the pack containing the object at bit position 0).

  • If pack(o1) ≠ pack(o2), then sort the two objects in descending order based on the pack ID.

  • Otherwise, pack(o1) = pack(o2), and the objects are sorted in pack-order (i.e., o1 sorts ahead of o2 exactly when offset(o1) < offset(o2)).

In short, a MIDX's pseudo-pack is the de-duplicated concatenation of objects in packs stored by the MIDX, laid out in pack order, and the packs arranged in MIDX order (with the preferred pack coming first).


请参见 Git 2.33 中的 https://github.com/git/git/commit/88617d11f9d2ee1ea726cef4527d676a9a46fa63:`multi-pack-index: fix potential segfault without sub-command`。 - VonC
Git 2.34修复了自Git 2.32以来的性能回归问题:https://github.com/git/git/commit/1ea5e46cb96d17c3b3927b4eff9765183cf87f8d - VonC

0
关于MIDX(“Multi-Pack-Index”,请参见此处),请确保使用Git 2.36+:
在Git 2.36(2022年第二季度)中已经修复了一个导致多包位图和对象顺序不同步的错误,从而使.midx数据损坏的bug。

请查看 提交 f8b60cf, 提交 7f514b7, 提交 a80f0f9, 提交 791170f, 提交 f0ed59a, 提交 90a8ea4, 提交 09a7799, 提交 95e8383, 提交 61fd31a (2022年1月25日) 由 Taylor Blau (ttaylorr) 提交。
(由 Junio C Hamano -- gitster -- 合并于 提交 f2cb46a, 2022年2月16日)

midx:当存在RIDX块时读取

签名作者:Taylor Blau
审核者:Derrick Stolee
审核者:Jonathan Tan

当MIDX包含新的RIDX块时,确保从其中读取反向索引而不是磁盘上的.rev文件。
由于我们需要在MIDX本身中编码对象顺序以确保正确性,因此在MIDX之外再存储相同的数据是没有意义的。

因此,这个补丁停止了写入单独的.rev文件,并从MIDX本身中读取它。
由于RIDX块的格式与.rev文件中的数据完全相同,因此可以用相对较少的新代码来实现这一点。
换句话说,我们可以通过将revindex_data字段指向MIDX的反向索引块而不是.rev文件来实现这一点,而不需要进行任何其他更改。

注意:[RIDX 文档/技术/包格式.txt][7]
[Optional] Bitmap pack order (ID: {'R', 'I', 'D', 'X'})

A list of MIDX positions (one per object in the MIDX, num_objects in total, each a 4-byte unsigned integer in network byte order), sorted according to their relative bitmap/pseudo-pack positions.


"git multi-pack-index repack/expire"(man) 用于将无法访问的垃圾数据重新打包成一个新的包,这个问题已经在 Git 2.39(2022年第四季度)中得到了修复。

请查看 提交记录b62ad56,提交记录0a8e561,提交记录cb6c48c,提交记录d9f7721,提交记录757d457,提交记录2a91b35,提交记录2699542 (2022年9月19日) ,由 Taylor Blau (ttaylorr) 提交。
(由 Junio C Hamano -- gitster -- 合并于 提交记录a215853, 2022年10月10日)

midx.c:使用repack --batch-size=0避免杂物包

签名作者:Taylor Blau

git multi-pack-index(man)repack子命令会创建一个新的打包文件,将MIDX中包含的较小的打包文件聚合到一起,直到达到给定的--batch-size
--batch-size=0时,这会指示MIDX内置程序将MIDX中包含的所有内容重新打包成一个单独的打包文件。
与之前的提交类似,此步骤中重新打包垃圾文件的内容是不可取的。
为了同样的原因,教会repack--batch-size=0时忽略任何垃圾文件。

0
除了我的上一个答案之外,Git 2.34(2021 年第四季度)添加了一个新功能。
在此之前,可达性位图文件仅用于单个数据包生成,但现在 Git 2.34 学会了为跨多个数据包的历史生成位图。
查看提交 73cd7d9提交 bfbb60d(2021年9月9日),以及提交 eb6e956提交 d3f17e1(2021年8月31日)由Jeff King (peff)
查看提交 2d59597提交 9387fbd提交 ff1e653提交 4b58b6f提交 e255a5e提交 c51f5a6提交 b1b82d1提交 aeb4657提交 c528e17提交 0f533c7提交 a5f9f24提交 711260f提交 6b4277e提交 ed18462提交 9bb6c2e提交 177c0d6提交 5d3cd09提交 f5909d3提交 426c00e提交 73ff4ad(2021年8月31日),提交 f57a739(2021年9月1日),以及提交 917a54c提交 1d7f7f2提交 3ba3d06提交 fa95666(2021年8月24日)由Taylor Blau (ttaylorr)
(由Junio C Hamano -- gitster --提交 0649303中合并,2021年9月20日)

midx:在未指定包时推断首选包 已签署:Taylor Blau
9218c6a(“midx: 允许将包标记为首选”,2021-03-30,Git v2.32.0-rc0 -- merge)中,多包索引代码学会了如何选择一个包,其中所有重复的对象都被选择。
也就是说,如果一个对象出现在多个包中,在根据其他规则(如包的修改时间和readdir()顺序)进行决策之前,选择首选包中的副本。
不指定首选包可能会导致多包可达性位图出现严重问题,因为这些位图依赖于至少有一个包中选择了所有重复对象。
没有这样的包会导致pack-objects中的代码无法重用包(例如,该代码假设从同一个包中发送的pack块中的增量对象将有其基对象从同一个包中发送)。
那么为什么不标记一个包作为首选会导致问题呢?原因大致如下:
当处理重复对象时,通过midx_oid_compare()进行排序以解决冲突,该函数按照OID、首选性、包的修改时间和最后包ID的顺序进行排序。
伪包顺序(在Documentation/technical/pack-format.txt的“multi-pack-index reverse indexes”部分描述)由midx_pack_order()计算,并按照包ID和包偏移进行排序,首选包排在前面。
但是!包ID来自于在add_pack_to_midx()中递增包计数的回调函数for_each_file_in_pack_dir(),这意味着包ID按照readdir()顺序分配。
当指定了首选包时,所有这些都能正常工作,因为重复对象会正确地解决为首选包中的副本,并且首选包在对象顺序中排在第一位。
“排在第一位”非常关键,因为位图代码依赖于找出MIDX伪包顺序中持有第一个对象的包,以确定哪个包是首选包。
但是,如果我们没有指定首选包,并且在readdir()顺序中排在第一位的包也不具有最低的时间戳,那么可能会导致该包(在伪包顺序中排在第一位,位图代码将将其视为首选包)未能解决所有重复对象的问题,从而导致错误。
修复方法很简单:在没有指定首选包时选择一个(半任意的、非空的)首选包。
这将强制该包解决重复项,并且(至关重要的是)在伪包顺序中排在第一位。
不幸的是,无法以可移植的方式测试此行为,因为它依赖于POSIX不保证的readdir()顺序。
(请注意,多包可达性位图尚未实现;因此从这个意义上说,此补丁修复了尚不存在的错误。
但是通过提前使用此补丁,我们可以防止该错误发生。)
注意:另请参阅 Git 2.38(2022 年第三季度)中的新设置 git -c push.useBitmaps=false push,以禁用 git push 的打包功能。

使用 Git 2.42(2023 年第三季度),当其中一个 MIDX 文件损坏时,git repack 更加健壮。

因此,在该场景下您将看到更少的 "could not open pack" 错误消息。

请参阅 commit 06f3867(2023 年 6 月 7 日)由 Taylor Blau (ttaylorr) 提交。
(合并者为 Junio C Hamano -- gitster --commit 1d15be3,2023 年 6 月 23 日)

pack-bitmap.c:在加载MIDX的包失败时优雅降级

签名:Taylor Blau

打开MIDX位图时,我们的Pack-Bitmap机制会急切地对MIDX中包含的每个pack调用prepare_midx_pack()
这样做是为了填充MIDX所持有的struct packed_git指针数组,以便我们在load_reverse_index()中稍后调用它,因为它对MIDX的每个pack都调用load_pack_revindex(),并要求调用者提供一个指向struct packed_git的指针。
当打开其中一个pack失败时,Pack-Bitmap代码将使用die()表示无法打开MIDX中的某个pack。
这表明MIDX与当前存储库的状态存在某种问题。
在这种情况下,确实无法利用MIDX位图来加快可达性遍历的速度。
然而,这并不意味着我们完全不能执行可达性遍历。
在其他故障模式下,同样的函数调用warning(),然后返回-1,向其调用者(open_bitmap())指示我们应该查找位图(如果有可用的),或者执行不使用位图的正常对象遍历。
没有理由导致我们崩溃。
如果我们继续执行(像这个补丁一样跳转到cleanup),完全避免使用位图,我们可以再次尝试查询MIDX,这也会失败。
但是当尝试调用fill_midx_entry()失败时,它还会返回其失败的信号,并提示调用者尝试在其他位置找到该对象。
换句话说,在存在损坏的MIDX的情况下,正常的对象遍历机制仍然可以正常工作,因此没有理由在此情况下中止MIDX位图机制,当我们可以轻松继续执行。
请注意,理论上我们可以在调用reprepare_packed_git()后再次尝试加载MIDX位图。
尽管prepare_packed_git()代码小心地避免添加我们已经拥有的pack,但prepare_midx_pack()不会这样做。
因此,如果我们在一个旧的MIDX上部分调用了prepare_midx_pack(),然后在包含一些相同pack的新的MIDX上再次尝试,我们将得到一个通过->next指针形成的循环。
目前,让我们尽可能简单地,在检测到旧的MIDX时回退到非位图代码,以便可以仔细实施上述完整修复方案。

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