bigywater.tech
Git · 23

repack 的参数:-a、-d、-f 各改变什么

git repack 的 -a、-d、-f 到底各改变什么?这篇把同一个起点复制七份,每份用一组选项,记录松散对象数、pack 数和大小、不可达对象的去向,还看了 cruft pack 的文件结构。

丢弃
-a -d 之后 pack 里不可达的提交 X 没了;-A、-k、--cruft 都保住了它
81,689 → 19,756
同一个没有 delta 的起点,-a -d 与 -a -d -f 重新打包后的字节数
.mtimes
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: 2

git repack 只把还没进 pack 的可达对象打成一个新 pack,旧 pack 与松散对象都不动。多跑几次就会得到一堆小 pack。

-d

实测 · 本机执行输出
  命令                                      松散  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。不可达的松散对象没有被打包,所以保留。

-a 与 -A

实测 · 本机执行输出
  起点合计 301,898 字节;-a -d:81,689;-a -d -f(重算 delta):19,756

-a 把所有可达对象重新合成一个 pack;配合 -d 删掉旧 pack,旧 pack 里不可达的对象就这样消失了。-A 则把这些不可达对象还原成松散对象,留给之后的 prune 按过期时间清理。-k 把它们一起收进新 pack。

-f

实测 · 本机执行输出
  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 搜索,慢得多。

cruft pack

实测 · 本机执行输出
  $ 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 判断,不用逐个看文件时间。

分步演示

repack 的选项
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动(pack.threads=1,单线程,结果可复现;我在没固定线程数时发现 -a -d -f 的结果在两次运行间会不同,所以固定了)。七个命令都在同一份起点的复制上各运行一次;起点由 git repack -adf --window=0 --depth=0、git repack -d、git branch -D 与 git reflog expire 构造;X 是 tmp 分支上的一个提交(含 3 个对象),UB 是 git hash-object -w 写入的松散 blob
    • 没有验证:-a -d 不加 -f 的结果小于起点、却大于 -f 的原因(只记录现象);-l(跳过 alternates)、--geometric、-b(位图)、--unpack-unreachable 等其它选项;--cruft-expiration 的过期行为(见 prune 一篇);多个 pack 带 .keep 文件时的行为
    京ICP备2026067502号-1京公网安备11010502063487号