欢迎其他关于优化打包或内存管理的建议。
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 日)
签字人: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 日)
签字人: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 日)
签字人: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 配置被忽略,因为打包位图工具仅对单个打包文件有用。
还有:
报告人: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 日)
签字人: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 日)支持>
帮助: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 日)
合着者: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。
简而言之,MIDX 的伪包是
MIDX 存储的包中的对象,按包顺序排列,以及
以 MIDX 顺序排列的包(首选包在前)。