git maintenance 把“后台整理仓库”拆成一个个任务。每个任务到底改了仓库的什么?不指定任务时会跑哪些?--auto 的阈值怎么算?这篇每个任务单独跑一遍,记录前后的松散对象、pack、提交图、ref 的变化,再用 GIT_TRACE 看各个 strategy 实际启动了哪些子命令。
gc → 可用 commit-graph → 可用 prefetch → 可用 loose-objects → 可用 incremental-repack → 可用 pack-refs → 可用 reflog-expire → 可用 rerere-gc → 可用 worktree-prune → 可用 geometric-repack → 可用 bogus → error: 'bogus' is not a valid task
gc、commit-graph、prefetch、loose-objects、incremental-repack、pack-refs、reflog-expire、rerere-gc、worktree-prune、geometric-repack 这 10 个名字在这台 git 上都可以用 --task= 指定,不认识的名字报错。
任务 松散对象 pack数 提交图 midx 松散ref packed_refs (起点) 664 0 False False 4 False loose-objects 664 1 False False 4 False loose-objects(第 2 次)0 1 False False 4 False commit-graph 664 0 True False 4 False pack-refs 664 0 False False 0 True incremental-repack 664 0 False False 4 False geometric-repack 0 1 False True 4 False gc 0 1 True False 0 True
用同一个仓库的副本每次只跑一个任务:loose-objects 先把松散对象打包,下一次才清理它们;commit-graph 写提交图;pack-refs 把松散 ref 收进 packed-refs;geometric-repack 打包并写 midx;gc 一次做完大部分工作;没有 pack 时 incremental-repack 无事可做。
$ git for-each-ref --format="%(refname)" refs/remotes refs/prefetch | tr "\n" " "; echo "(prefetch 之前)" refs/remotes/origin/HEAD refs/remotes/origin/b1 refs/remotes/origin/b2 refs/remotes/origin/main (prefetch 之前) $ git for-each-ref --format="%(refname)" refs/remotes refs/prefetch | tr "\n" " "; echo "(prefetch 之后)" refs/prefetch/remotes/origin/b1 refs/prefetch/remotes/origin/b2 refs/prefetch/remotes/origin/main refs/prefetch/remotes/origin/newwork refs/remotes/origin/HEAD refs/remot $ git fetch -q; git for-each-ref --format="%(refname)" refs/remotes refs/prefetch | tr "\n" " "; echo "(之后真的 git fetch 的结果:refs/remotes 更新;refs/prefetch 在下次 prefetch 时会被清理)" refs/prefetch/remotes/origin/b1 refs/prefetch/remotes/origin/b2 refs/prefetch/remotes/origin/main refs/prefetch/remotes/origin/newwork refs/remotes/origin/HEAD refs/remot
prefetch 把远程的分支下载到 refs/prefetch/<远程>/,不改动 refs/remotes 和本地分支,它把远程的新对象提前下载好;下一次 git fetch 因此节省多少没有测。
strategy (不设置): (手动 git maintenance run 的默认)
git pack-refs --all --prune
git reflog expire --all
git repack -d -l --cruft --cruft-expiration=2.weeks.ago --quiet --writ
git multi-pack-index write --stdin-packs --no-progress --preferred-pac
git commit-graph write --split --reachable --no-progress
git worktree prune --expire 3.months.ago
git rerere gc
strategy gc:
git pack-refs --all --prune
git reflog expire --all
git gc --quiet --no-detach --skip-foreground-tasks
git repack -d -l -q --cruft --cruft-expiration=2.weeks.ago
git prune --expire 2.weeks.ago --no-progress
git worktree prune --expire 3.months.ago
git rerere gc
strategy geometric:
git pack-refs --all --prune
git reflog expire --all
git repack -d -l --geometric=2 --quiet --write-midx
git multi-pack-index write --stdin-packs --no-progress --preferred-pac
git commit-graph write --split --reachable --no-progress
git worktree prune --expire 3.months.ago
git rerere gc
strategy incremental:
git pack-refs --all --prune
git reflog expire --all
git gc --quiet --no-detach --skip-foreground-tasks
git repack -d -l -q --cruft --cruft-expiration=2.weeks.ago
git prune --expire 2.weeks.ago --no-progress
git worktree prune --expire 3.months.ago
git rerere gc
strategy bogus: fatal: unknown maintenance strategy: 'bogus'不带 --task 时,maintenance.strategy 决定任务集合。手动运行默认是 geometric 风格;gc 与手动运行的 incremental 走 git gc;写错取值直接报 fatal。
99 个提交没有提交图 → git maintenance run --auto --task=commit-graph: 什么也没做 100 个提交没有提交图 → git maintenance run --auto --task=commit-graph: 写了提交图 全仓库 99 个松散对象(默认阈值 100)→ git maintenance run --auto --task=loose-objects: 什么也没做 全仓库 100 个松散对象(默认阈值 100)→ git maintenance run --auto --task=loose-objects: 打包了 全仓库 9 个松散对象(loose-objects.auto=10)→ git maintenance run --auto --task=loose-objects: 什么也没做 全仓库 10 个松散对象(loose-objects.auto=10)→ git maintenance run --auto --task=loose-objects: 打包了
--auto 让每个任务先判断是否达到阈值:提交图任务数不在提交图里的提交(默认 100),松散对象任务数全仓库的松散对象(默认 100),都是达到才运行。
$ git config --get-regexp "^maintenance" || echo "(没有 maintenance 配置)" maintenance.auto false $ git config --get-regexp "^maintenance" | sort maintenance.auto false maintenance.commit-graph.enabled true maintenance.commit-graph.schedule daily maintenance.loose-objects.enabled true $ GIT_TRACE=1 git maintenance run 2>&1 | grep 'trace: run_command' | sed -E 's/.*run_command: //' | grep -v 'git pack-objects' | cut -c1-70 git pack-refs --all --prune git reflog expire --all git prune-packed --quiet git repack -d -l --geometric=2 --quiet --write-midx git multi-pack-index write --stdin-packs --no-progress --preferred-pac git commit-graph write --split --reachable --no-progress git worktree prune --expire 3.months.ago git rerere gc
maintenance.<任务>.enabled 在策略的基础上增减任务,maintenance.<任务>.schedule 决定 --schedule 模式下的频率。