bigywater.tech
Git · 77

git maintenance 的任务

git maintenance 把“后台整理仓库”拆成一个个任务。每个任务到底改了仓库的什么?不指定任务时会跑哪些?--auto 的阈值怎么算?这篇每个任务单独跑一遍,记录前后的松散对象、pack、提交图、ref 的变化,再用 GIT_TRACE 看各个 strategy 实际启动了哪些子命令。

10 个
git 2.54.0 认识的 maintenance 任务名数量
100
commit-graph.auto 与 loose-objects.auto 的默认阈值:99 不动,100 才运行
2 次
loose-objects 要运行两次,第一次打包,第二次才清理松散对象

有哪些任务

实测 · 本机执行输出
  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 无事可做。

prefetch

实测 · 本机执行输出
  $ 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

实测 · 本机执行输出
  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。

--auto 的阈值

实测 · 本机执行输出
  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 模式下的频率。

分步演示

git maintenance 的任务
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。任务名的“可用”来自对每个名字执行 git maintenance run --task=<名>;各任务的前后状态来自同一个仓库的副本;子命令来自 GIT_TRACE 的 run_command 行(过滤了 pack-objects);实验环境的全局配置里 maintenance.auto=false
    • 没有验证:git maintenance start / register 与后台调度(launchd);--schedule 的小时、每天、每周触发;gc 任务在 gc.auto 阈值上的行为;incremental-repack 在已有多个 pack 时的批量大小;prefetch 在 refs/prefetch 下旧引用何时被清理;is-needed 子命令的退出码语义;大仓库下各任务的耗时;并发运行时的锁文件
    京ICP备2026067502号-1京公网安备11010502063487号