【问题标题】:Is repacking a repository useful for large binaries?重新打包存储库对大型二进制文件有用吗?
【发布时间】:2018-06-23 00:21:37
【问题描述】:

我正在尝试将大型历史记录从 Perforce 转换为 Git,并且一个文件夹(现在是 git 分支)包含大量大型二进制文件。我的问题是运行git gc --aggressive 时内存不足。

我的主要问题是重新打包存储库是否可能对大型二进制文件产生任何有意义的影响。再压缩 20% 会很棒。 0.2% 不值得我努力。如果没有,我会按照here 的建议跳过它们。

作为背景,我成功地使用git p4 在我满意的状态下创建了存储库,但这在幕后使用了git fast-import,所以我想在正式发布之前优化存储库,并确实做出任何提交自动触发了缓慢的gc --auto。目前裸机状态约为 35GB。

从概念上讲,所讨论的二进制文件似乎是嵌入式设备中使用的供应商固件。我认为在 400-700MB 范围内大约有 25 个,在 20-50MB 范围内可能还有几百个。它们可能是磁盘映像,但我不确定。随着时间的推移,有各种版本和文件类型,我经常看到.zip、tgz 和.simg 文件。因此,我预计原始代码会有很大的重叠,但我不确定此时实际文件看起来有多相似,因为我相信这些格式已经被压缩了,对吧?

这些二进制文件包含在一个(旧)分支中,很少使用(甚至质疑版本控制是否有效,但超出了范围)。当然,该分支的性能不需要很好。但我希望存储库的其余部分是合理的。

欢迎提出其他关于优化打包或内存管理的建议。我承认我并不真正理解在链接问题上讨论的各种 git 选项。我也不真正了解--window 和--depth 标志在git repack 中的作用。但主要问题是重新打包二进制文件本身是否有意义。

【问题讨论】:

  • Git 2.20(2018 年第四季度)应该优化包文件,使 repo 更加健壮。见my answer below。

标签: git compression


【解决方案1】:

我的主要问题是重新打包存储库是否可能对大型二进制文件产生任何有意义的影响。

这取决于它们的内容。对于您特别列出的文件:

我经常看到 .zip、tgz 和 .simg 文件。

Zipfiles 和 tgz(gzipped tar 存档)文件已经被压缩并且具有糟糕的(即高)Shannon entropy 值——这对于 Git 来说是可怕的——并且不会相互压缩。 .simg 文件可能是(我必须在这里猜测)Singularity disk image files;我不知道它们是否以及如何被压缩,但我会假设它们是。 (一个简单的测试是将一个输入压缩器,例如 gzip,看看它是否收缩。)

因此,我预计原始代码会有很大的重叠,但我不确定此时实际文件看起来有多相似,因为我相信这些格式已经被压缩了,对吧?

没错。因此,矛盾的是,将它们未压缩存储在 Git 中最终会导致更大的压缩。 (但打包可能需要大量内存。)

如果 [这可能是徒劳的],我会按照 here 的建议跳过它们。

那将是我在这里的第一个冲动。 :-)

我承认我并不真正理解在链接问题上讨论的各种 git 选项。我也不太明白--window 和--depth 标志在git repack 中的作用。

各种限制令人困惑(而且很多)。同样重要的是要意识到它们不会在克隆时被复制,因为它们位于.git/config 中,这不是一个提交的文件,因此新的克隆不会拾取它们。 .gitattributes 文件被复制到克隆上,新的克隆将继续避免打包不可打包的文件,所以这是更好的方法。

(如果你想深入了解细节,你会在the Git technical documentation 中找到一些。这并没有准确讨论窗口大小是什么,但它与 Git 用于 memory-map 对象的内存量有关选择可以很好地相互压缩的对象时的数据。有两个:一个用于一个包文件上的每个单独 mmap,一个用于所有包文件上的总聚合 mmap。链接中未提及:core.deltaBaseCacheLimit,即将使用多少内存来保存 delta 基数——但要理解这一点,您需要了解 delta 压缩和 delta 链,1 并阅读相同的技术文档。请注意,Git 将默认不尝试打包任何大小超过core.bigFileThreshold 的文件对象。各种pack.* 控件有点复杂:打包是多线程完成的,以尽可能利用所有CPU,并且每个线程可以使用大量内存。线程数限制总内存使用:如果一个线程正在运行o 使用 256 MB,8 个线程可能使用 8*256 = 2048 MB 或 2 GB。位图主要加快从繁忙的服务器中获取的速度。)


1它们并没有那么复杂:当一个对象说“获取对象 XYZ 并应用这些更改”,但对象 XYZ 本身说“获取对象 PreXYZ 并应用这些更改”时,就会出现增量链.对象 PreXYZ 也可以取另一个对象,依此类推。 delta base 是这个列表底部的对象。

【讨论】:

  • 谢谢!这就是我害怕的……哦,好吧,至少这次它运行得更快了。那是一本引人入胜的文档阅读。我绝对不能说我理解它,但我得到了一般的想法,但还不足以想要篡改这些价值观。但无论如何,排除大文件就可以了。跟进:有没有一种简单的方法可以知道哪些二进制类型是可压缩/低熵的?
  • 最佳答案(不是我的答案;有关详细信息,请参阅this one 和方程式)是here。如果您不想编写自己的代码,那么我不确定这是否符合“简单”的条件,但一般来说,如果您将低熵文件提供给压缩器,它应该会缩小很多,如果您将一个高熵文件提供给一个文件,它应该不会缩小太多(甚至可能会变大)。
【解决方案2】:

欢迎其他关于优化打包或内存管理的建议。

Git 2.20(2018 年第四季度)将有一个:当存储库中有太多包文件(不推荐)时,在其中查找对象需要查阅许多包 .idx 文件; 引入了一种新机制,可以将所有这些.idx 文件合并到一个文件中。

见commit 6a22d52、commit e9ab2ed、commit 454ea2e、commit 0bff526、commit 29e2016、commit fe86c3b、commit c39b02a、commit 2cf489a、commit 2cf489a、commit 6d68e6a(2018 年 8 月 20 日 3 月 5 日)、@9876 2018 年 7 月)Derrick Stolee (derrickstolee)。
(由 Junio C Hamano -- gitster -- 合并,commit 49f210f,2018 年 9 月 17 日)

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

在运行 'git pack-objects --local' 时,我们希望避免打包备用对象中的对象。
目前,我们使用 packed_git_mru 列表检查这些对象,该列表不包括多包索引所涵盖的包文件。

有一个新设置:

core.multiPackIndex::

使用 multi-pack-index 文件使用单个索引跟踪多个包文件。

还有multi-pack index is explained here 和Documentation/technical/multi-pack-index.txt:

多包装索引 (MIDX) 设计说明

Git 对象目录包含一个“pack”目录,其中包含:

  • packfiles(带有后缀“.pack”)和
  • pack-indexes(后缀为“.idx”)。

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

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

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

multi-pack-index(简称MIDX)将对象列表及其偏移存储到多个包文件中。
它包含:

  • 包文件名列表。
  • 对象 ID 的排序列表。
  • 第 i 个对象 ID 的元数据列表,包括:
  • 值 j 引用第 j 个包文件。
  • 对象的第 j 个包文件中的偏移量。
  • 如果需要大偏移量,我们使用另一个大的列表 类似于第 2 版 pack-indexes 的偏移量。

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


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

请参阅commit 3612c23(2019 年 7 月 1 日)和 commit b526d8c、commit 10bfa3f、commit d274331、commit ce1e4a1、commit 2af890b、commit 19575c7、commit d01bf2e、commit dba6175、commit dba6175、@987 commit 81efa16、commit 8434e85(2019 年 6 月 10 日)Derrick Stolee (derrickstolee)。
帮助者:Johannes Schindelin (dscho)。
(由 Junio C Hamano -- gitster -- 合并于 commit 4308d81,2019 年 7 月 19 日)

multi-pack-index:准备/实施“expire”子命令

多包索引跟踪包文件集合中的对象。
每个对象只有一个副本被索引,使用包文件的修改时间来确定决胜局。
可能有一个没有引用对象的包文件,因为所有对象在较新的包文件中都有重复。

向内置的 multi-pack-index 引入一个新的“expire”子命令。
此子命令将删除这些未使用的包文件并重写多包索引以不再引用这些文件
。

“git multi-pack-index expire”子命令:

  • 查看现有的多包索引,
  • 计算每个包文件中引用的对象数,
  • 删除没有引用对象的包文件,并且
  • 重写多包索引以不再引用这些包。

Documentation:

expire:

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

还有:

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

在多包索引有用的环境中,这是由于许多包文件和无法将对象存储重新打包到单个包文件中。但是,很可能这些包文件中的许多都相当小,并且可以重新打包成一个稍微大一点的包文件而无需太多努力。
确保对象存储的高可用性以及重新打包操作不会中断并发的git 命令也很重要。

向“git multi-pack-index”引入一个“repack”子命令,该子命令采用“--batch-size”选项。

该子命令将检查多包索引以查找大小小于批处理大小的引用包文件,直到收集大小总和大于批处理大小的包文件列表。
然后,将创建一个新的包文件,其中包含多包索引引用的那些包文件中的对象。

生成的包实际上可能小于批量大小,因为 压缩以及包文件中可能存在在其他包文件中具有重复副本的对象这一事实。

“git multi-pack-index repack”命令可以将批量大小设为零,这会创建一个包含多包索引中所有对象的新包文件。

使用 0 批量大小与标准的“git repack”命令非常相似,只是我们不删除旧包,而是依靠新的多包索引来阻止新进程阅读旧包。
这不会中断当前正在读取基于旧多包索引的旧包的其他 Git 进程。

第一个“repack”命令将创建一个新的包文件,之后的“expire”命令将删除旧的包文件,因为它们不再包含多包中的任何引用对象-索引。

Documentation:

repack:

创建一个新的包文件,其中包含多包索引引用的小包文件中的对象。
如果--batch-size=<size> 参数给定的大小为零,则创建一个包含多包索引引用的所有对象的包。

对于非零批量大小:

  • 通过从最旧到最新检查包来选择包文件,
  • 通过计算多包索引引用的包中的对象数量来计算“预期大小”,
  • 然后除以包中的对象总数,然后
  • 乘以包装大小。

我们选择预期大小低于批量大小的包,直到包集的总预期大小至少是批量大小。

  • 如果总大小未达到批量大小,则什么也不做。
  • 如果创建了新的包文件,请重写 multi-pack-index 以引用新的包文件。
    稍后运行“git multi-pack-index expire”将删除属于该批次的包文件。

使用 Git 2.25(2020 年第一季度),生成多包索引的代码学会了显示(或不显示)进度指示器。

这对于大型二进制文件很有用。

参见commit 680cba2、commit 64d80e7、commit ad60096、commit 8dc18f8、commit 840cef0、commit efbc3ae(2019 年 10 月 21 日)William Baker (wjbaker101)。
(由 Junio C Hamano -- gitster -- 合并commit 8f1119b,2019 年 11 月 10 日)

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

签字人:William Baker

将--[no-]progress 选项添加到git multi-pack-index。
当 multi-pack-index 显示进度时,将 MIDX_PROGRESS 标志传递给子命令函数。

在144d703中的'verify'中添加了进度功能(“multi-pack-index:在'验证'期间报告进度”,2018-09-13,Git v2.20.0-rc0 -- merge列出在batch #3),但某些子命令未更新以显示进度,并且忽略了选择退出的能力。


不要忘记阅读Documentation/technical/pack-format.txt,其中包括多包索引 (MIDX) 文件格式说明。 在 Git 2.25.1(2020 年 2 月)中,有一个文档修复。

参见 Johannes Berg (berghallen) 的 commit eb31044(2020 年 2 月 7 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 0410c2b,2020 年 2 月 12 日)

pack-format:正确的多包索引描述

签字人:Johannes Berg
签字人:Derrick Stolee

multi-pack-index 的描述包含一个小错误,如果所有偏移量都是< 2^32,那么将没有LOFF 块,不仅它们都是< 2^31(因为最高位是仅在实际需要时才需要作为“LOFF-escape”。)

更正这一点,并澄清在这种情况下,只有达到2^31-1 的偏移量才能存储在OOFF 块中。

documentation for pack-format 现在包括:

2:包内的偏移量。

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


在 Git 2.27(2020 年第二季度)之前,当输入不记录任何对象的 midx(Multi-Pack-Index)时,一些代码路径试图从 0 循环到 (num_objects-1),,由于整数算术环绕,这使得它变得毫无意义数组访问越界的操作。

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

参见Damien Robert (damiens-robert) 的commit 796d61c(2020 年 3 月 28 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 8777ec1,2020 年 4 月 22 日)

midx.c: 修复整数下溢

签字人:Damien Robert

在验证包含 0 个对象的 midx 索引时, m->num_objects - 1 下溢并回绕到4294967295。

通过检查 midx 是否包含至少一个 oid 以及在没有包文件时我们不编写任何 midx 来解决此问题。

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


在 Git 2.27(2020 年第 2 季度)中,“git multi-pack-index repack”被教导要尊重一些 repack.* 配置变量。

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

midx:教“git multi-pack-index repack”荣誉“git repack”配置

签字人: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”选项现在用于指定将非常小的包文件收集到一个直到总大小大致超过它。

参见Derrick Stolee (derrickstolee) 的commit 1eb22c7(2020 年 8 月 11 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 9e8c754,2020 年 8 月 24 日)

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

签字人:Derrick Stolee
审核人:Taylor Blau

'git multi-pack-index repack(man)' 的 --batch-size= 选项旨在限制重新打包完成的工作量。在大型存储库的情况下,此命令应重新打包许多小包文件,但不理会大包文件。大多数情况下,存储库有一个来自 'git clone(man) ' 操作的大包文件和来自增量 'git fetch(@987654405 的较小包文件的数量@) ' 操作。

“--batch-size”的问题在于,如果生成的包文件的预期大小太小,它还会防止重新打包。

这是为了避免频繁搅动小包文件,但当存储库为“中等”大小时,它主要会引起混乱。
也就是说,不像 Windows 操作系统存储库那样庞大,但也不会小到这种增量重新打包没有价值。

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

这种新策略应该成功地在这些“中等”大小的存储库中保持较小的包文件数量。对流失的关注可能并不有趣,因为对其的真正控制是运行重新打包命令的频率。

git multi-pack-index 现在包含在其man page 中:

我们选择预期大小低于批量大小的包,直到包集的总预期大小至少达到批量大小,或者考虑所有包文件。
如果只选择了一个包文件,则什么也不做。
如果创建了新的包文件,请重写多包索引以引用新的包文件。

稍后运行 'git multi-pack-index expire' 将删除 是这批的一部分。


当包文件被“git repack”(man) 删除时,multi-pack-index 被清除;在 Git 2.29(2020 年第 4 季度)中,通过首先检查 midx 是否实际上指的是不再存在的包,该代码被教导要不那么激进。

请参阅commit 59552fb(2020 年 8 月 28 日)和 commit e08f7bb(2020 年 8 月 25 日)Taylor Blau (ttaylorr)。
(由 Junio C Hamano -- gitster -- 合并于 commit 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 列在batch #1), 'git repack (man) '如果从对象存储中添加或删除了包,则学会了删除多包索引文件。

这种机制有点过于急切,因为只有在“git repack(man)”删除了 MIDX 引用的包时才需要删除 MIDX。
在 MIDX 之外添加一个包不需要使 MIDX 无效,同样地,对于删除一个 MIDX 不知道的包。

教导 'git repack(man) '通过加载 MIDX 并检查 MIDX 是否知道要删除的包来检查这一点。

添加了一项新测试,以表明当 MIDX 已知的两个包都标记为 .keep 时,MIDX 被单独放置,但它不知道的两个包被删除并合并为一个新包。


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

见commit 3007752(2021 年 3 月 30 日)Jeff King (peff)。
见commit 38ff7ca,commit a587b5a,commit f894081,commit b25fd24,commit 62f2c1b,commit 9f19161,commit 7240cc4,commit 9218c6a,commit 86d174b,@9876544435@,commit 690eb05,commit 690eb05,@98765 987654438@、commit cf1f538、commit f7c4d63(2021 年 3 月 30 日)Taylor Blau (ttaylorr)。
请参阅 Junio C Hamano (gitster) 的 commit 1187556(2021 年 2 月 24 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit e6b971f,2021 年 4 月 8 日)

Documentation/technical:描述多包反向索引

合着者:Jeff King
署名者:Jeff King
署名者:Taylor Blau

作为实现多包位图的先决条件,激发和描述多包反向索引的格式和顺序。

technical/pack-format 现在包含在其man page 中:

== 多包索引反向索引

类似于基于包的反向索引,多包索引也可以 用于生成反向索引。

而不是偏移量、pack- 和索引位置之间的映射,这个 对象在 MIDX 中的位置之间的反向索引映射,以及 该对象在 MIDX 描述的伪包中的位置 (即,多包反向索引的第 i 个条目保存 MIDX 第 i 个对象在伪包顺序中的位置)。

为了阐明这些订购之间的区别,请考虑多件装 可达性位图(尚不存在,但我们就是 朝这里建设)。每个位都需要对应一个对象 MIDX,因此我们需要从位位置到 MIDX 的有效映射 位置。

一种解决方案是让位在 oid-sorted 中占据相同的位置 MIDX 存储的索引。但是因为 oids 实际上是随机的,所以它们的 生成的可达性位图没有局部性,因此压缩 不良。 (这就是单包位图使用包的原因 排序,而不是 .idx 排序,目的相同。)

所以我们想为整个 MIDX 定义一个排序 包排序,具有更好的局部性(因此压缩更多 有效率的)。我们可以想象一个由串联创建的伪包 MIDX 中的所有包。例如,如果我们有一个三包 MIDX (a, b, c),分别有 10、15 和 20 个对象,我们可以想象一个 对象的排序,例如:

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

其中包的顺序由 MIDX 的包列表定义, 然后每个包中对象的顺序与 在实际的包文件中订购。 MIDX 中的对象按如下顺序排列以将 伪包。让pack(o) 返回选择o 的包 由 MIDX,并根据其数字 ID 定义包装的顺序 (由 MIDX 存储)。让offset(o)返回o的对象偏移量 在pack(o) 内。然后,比较o1和o2如下:

  • 如果首选pack(o1) 和pack(o2) 之一,而另一个 不是,然后优先排序。

(这是一个细节,允许 MIDX 位图确定哪个 pack 应该由 pack-reuse 机制使用,因为它可以询问 包含位位置 0 的对象的包的 MIDX。

  • 如果pack(o1) ≠ pack(o2),则将两个对象按降序排序 根据包 ID 排序。

  • 否则,pack(o1) = pack(o2),对象排序在 包装顺序(即,o1 排在o2 之前,恰好在offset(o1) < offset(o2) 时)。

简而言之,MIDX 的伪包是 MIDX 存储的包中的对象,按包顺序排列,以及 以 MIDX 顺序排列的包(首选包在前)。

【讨论】:

【解决方案3】:

除了我的previous answer,Git 2.34(2021 年第四季度)增加了一项新功能。

以前,可达性位图文件只能为单个包生成,但现在 Git 2.34 学会了为跨越多个包文件的历史生成位图。

请参阅commit 73cd7d9、commit bfbb60d(2021 年 9 月 9 日)和 commit eb6e956、commit d3f17e1(2021 年 8 月 31 日)Jeff King (peff)。
见commit 2d59597,commit 9387fbd,commit ff1e653,commit 4b58b6f,commit e255a5e,commit c51f5a6,commit b1b82d1,commit aeb4657,commit c528e17,commit a5f9f24,commit a5f9f24,commit a5f9f24,@987643375 987654339@、commit ed18462、commit 9bb6c2e、commit 177c0d6、commit 5d3cd09、commit f5909d3、commit 426c00e、commit 73ff4ad(2021 年 8 月 31 日)、commit f57a739、@97654347@(2021 年 9 月 1 日) commit 1d7f7f2、commit 3ba3d06、commit fa95666(2021 年 8 月 24 日)Taylor Blau (ttaylorr)。
(由 Junio C Hamano -- gitster -- 合并于 commit 0649303,2021 年 9 月 20 日)

midx: 在没有给出时推断首选包

签字人:Taylor Blau

在9218c6a(“midx:允许将包标记为首选”,2021-03-30,Git v2.32.0-rc0 -- merge)中,多包索引代码学会了如何选择从中选择所有重复对象的包。
也就是说,如果一个对象出现在多个包中,请在根据包 mtime 和 readdir() 顺序等其他规则打破平局之前选择首选包中的副本。

不指定首选包可能会导致多包可达性位图出现严重问题,因为这些位图依赖于至少一个包,其中所有重复项都被选中。
没有这样的包会导致包对象中的代码出现问题,无法逐字重用包(例如,该代码假定逐字发送的包块中的增量对象将从同一个包发送其基础对象)。

那么为什么不将包标记为首选会导致问题呢?原因大致如下:

  • 通过根据 midx_oid_compare() 进行排序来打破关系(在处理重复对象时),它按 OID、preferred-ness、pack mtime 和最后 pack ID 对对象进行排序(稍后会详细介绍)。
  • 伪包装顺序(在“多包装索引反向索引”部分下的 Documentation/technical/pack-format.txt 中描述)由 midx_pack_order() 计算,并按包装 ID 和包装偏移量排序,使用首选包装优先排序。
  • 但是!包 ID 来自于增加 add_pack_to_midx() 中的包计数,这是对 for_each_file_in_pack_dir() 的回调,这意味着包 ID 是按 readdir() 顺序分配的。

指定首选包时,所有这些都可以正常工作,因为重复的对象被正确解析,有利于首选包中的副本,并且首选包在对象顺序中排在第一位。

“先排序”很关键,因为位图代码依赖于找出哪个包包含 MIDX 的伪包顺序中的第一个对象,以确定首选哪个包。

但是如果我们没有指定首选包,并且在 readdir() 顺序中排在第一位的包也没有最低时间戳,那么该包(按照伪包顺序排在第一位的包)可能,位图代码会将其视为首选对象)是否没有将所有重复的对象都解析为有利于它的对象,从而导致损坏。

解决方法很简单:在未指定时选择(半任意、非空)首选包。
这迫使该包有利于解决重复问题,并且(关键地)首先按伪包顺序排序。
不幸的是,无法对这种行为进行便携式测试,因为它依赖于 POSIX 不保证的 readdir() 顺序。

(请注意,多包可达性位图尚未实现;因此从这个意义上说,此补丁正在修复一个尚不存在的错误。
但是通过预先安装这个补丁,我们可以防止这个错误出现。)

【讨论】:

    【解决方案4】:

    关于 MIDX(“Multi-Pack-Index”,presented here),请务必使用 Git 2.36+:

    Git 2.36(2022 年第二季度)修复了导致多包位图和对象顺序不同步、导致 .midx 数据损坏的错误。

    请参阅commit f8b60cf、commit 7f514b7、commit a80f0f9、commit 791170f、commit f0ed59a、commit 90a8ea4、commit 09a7799、commit 95e8383、commit 61fd31a(2022 年 1 月 25 日)@9876。 br>(由 Junio C Hamano -- gitster -- 合并于 commit 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 文件来实现这一点,而无需进行任何其他更改。

    注意:[RIDXDocumentation/technical/pack-format.txt][7]

    [Optional] Bitmap pack order (ID: {'R', 'I', 'D', 'X'})
    

    MIDX 位置列表(MIDX 中的每个对象一个,总共 num_objects 个,每个都是按网络字节顺序排列的 4 字节无符号整数),根据它们的相对位图/伪包位置排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-03
      • 1970-01-01
      • 2013-12-06
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多