欢迎提供有关最佳打包或内存管理的其他建议。
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)
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日)
签名: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)
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日)
签署者: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日)
协助者: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 -c push.useBitmaps=false push
,用于禁用git push
的打包功能。 - VonC