git repack 的 -a、-d、-f 到底各改变什么?这篇把同一个起点复制七份,每份用一组选项,记录松散对象数、pack 数和大小、不可达对象的去向,还看了 cruft pack 的文件结构。
松散对象 7 个;pack 2 个(合计 301,898 字节);不可达的提交 X(在 pack 里)存在: True;不可达的 blob UB(松散)存在: True
$ ls .git/objects/pack/*.pack | wc -l; git count-objects -v | grep -E "^(count|in-pack|packs)"
2
count: 7
in-pack: 93
packs: 2git repack 只把还没进 pack 的可达对象打成一个新 pack,旧 pack 与松散对象都不动。多跑几次就会得到一堆小 pack。
命令 松散 pack pack 合计字节 cruft X(pack 里不可达) UB(松散不可达) (起点) 7 2 301,898 0 留着 留着 git repack 7 3 312,259 0 留着 留着 git repack -d 1 3 312,259 0 留着 留着 git repack -a -d 1 1 81,689 0 没了 留着 git repack -A -d 4 1 81,689 0 留着 留着 git repack -a -d -f 1 1 19,756 0 没了 留着 git repack -a -d -k 0 1 81,918 0 留着 留着 git repack -a -d --cruft 0 2 82,010 1 留着 留着
-d 之后删除变得多余的东西:已经被收进 pack 的松散对象,以及内容被完全包含的旧 pack。不可达的松散对象没有被打包,所以保留。
起点合计 301,898 字节;-a -d:81,689;-a -d -f(重算 delta):19,756
-a 把所有可达对象重新合成一个 pack;配合 -d 删掉旧 pack,旧 pack 里不可达的对象就这样消失了。-A 则把这些不可达对象还原成松散对象,留给之后的 prune 按过期时间清理。-k 把它们一起收进新 pack。
git repack -a -d X: 没了 松散对象 1,cruft 文件 0 git repack -A -d X: 留着 松散对象 4,cruft 文件 0 git repack -a -d --cruft X: 留着 松散对象 0,cruft 文件 1
-f(--no-reuse-delta)让 pack-objects 丢弃已有的 delta 选择、全部重算。本例从一个完全没有 delta 的起点出发,不加 -f 得到 81,689 字节,加 -f 得到 19,756 字节。代价是 -f 要重做所有对象的 delta 搜索,慢得多。
$ ls -l .git/objects/pack | awk 'NR>1 {print $5, $9}' | sed -E "s/pack-[0-9a-f]{40}/pack-<hash>/"
1184 pack-<hash>.idx
68 pack-<hash>.mtimes
321 pack-<hash>.pack
68 pack-<hash>.rev
3760 pack-<hash>.idx
81689 pack-<hash>.pack
436 pack-<hash>.rev
$ git count-objects -v | grep -E "^(count|in-pack|packs)"
count: 0
in-pack: 100
packs: 2--cruft 把不可达对象放进一个专门的 pack,并用 .mtimes 文件记录每个对象的时间,不再需要为每个不可达对象保留一个松散文件。过期清理时按 .mtimes 判断,不用逐个看文件时间。