Git rerere:复用已解决过的合并冲突

129 次浏览3 条回复

同一组分支反复合并或变基时,相似冲突可能需要多次做出相同修改。Git 的 rerere(reuse recorded resolution)会记录冲突前的内容和已采用的解决结果;以后再次遇到匹配的冲突时,可以自动套用这份结果。

环境:Git 2.39+;Linux 或 macOS;Bash;需要 mktemp。下面只操作新建的临时仓库。

root=$(mktemp -d)
trap 'rm -rf -- "$root"' EXIT
repo="$root/demo"

git init -b main "$repo"
git -C "$repo" config user.name "Rerere Demo"
git -C "$repo" config user.email "[email protected]"
git -C "$repo" config rerere.enabled true
git -C "$repo" config rerere.autoupdate true

printf 'mode=base\n' >"$repo/app.conf"
git -C "$repo" add app.conf
git -C "$repo" commit -m base

git -C "$repo" switch -c feature
printf 'mode=feature\n' >"$repo/app.conf"
git -C "$repo" commit -am feature

git -C "$repo" switch main
printf 'mode=main\n' >"$repo/app.conf"
git -C "$repo" commit -am main

# 第一次合并产生预期冲突。
if git -C "$repo" merge feature; then
  printf 'expected a conflict\n' >&2
  exit 1
fi

printf 'mode=combined\n' >"$repo/app.conf"
git -C "$repo" add app.conf
git -C "$repo" commit -m 'record resolution'

# 回到合并前,再制造同一冲突。
git -C "$repo" reset --hard HEAD^
if git -C "$repo" merge feature; then
  printf 'expected a conflict\n' >&2
  exit 1
fi

git -C "$repo" status --short
git -C "$repo" diff --cached -- app.conf

第二次 merge 仍会以非零状态报告冲突,但 rerere.autoupdate=true 会把先前的 mode=combined 解决结果写回并加入暂存区;git status --short 会把 app.conf 显示为已暂存修改。记录保存在当前仓库的 .git/rr-cache,不会修改其他仓库。

自动复用不等于自动判定语义正确。提交前应检查 git diff --cached 并运行项目测试;上下文变化较大时,Git 也可能无法匹配旧记录。若只希望写回工作区、不希望自动暂存,可以保持 rerere.enabled=true,同时不启用 rerere.autoupdate。

可以补一个撤回入口:如果 rerere.autoupdate 套用了不合适的旧结果,应在本次合并尚未提交时先检查并清除这次冲突对应的记录:

git -C "$repo" rerere status
git -C "$repo" rerere diff
git -C "$repo" rerere forget app.conf
git -C "$repo" status --short

status 用于确认正在跟踪的冲突路径,diff 展示当前解决结果相对冲突原始内容的变化;forget 会忘掉当前冲突在该路径上的既有解决记录,使其可以重新人工解决。完成修正后再执行 git add app.conf、git rerere 和项目测试,并用 git diff --cached 做最后确认。这样错误结果不会继续成为后续同类冲突的模板。

长期维护分支还要注意:rerere 记录不是默认永久保留的。执行 git rerere gc 时,已解决记录默认保留 60 天,未解决记录默认保留 15 天。发布分支隔几个月才合一次的话,可以只在当前仓库调大:

git -C "$repo" config gc.rerereResolved 180
git -C "$repo" config gc.rerereUnresolved 30
git -C "$repo" rerere gc

配置值的单位是天。团队如果依赖这项能力,也得留意 .git/rr-cache 是本地状态,换机器或重新克隆后不会跟着仓库历史回来。

再补一个很顺手的检查命令:开启 rerere.autoupdate 后,可以用 git rerere remaining 看还有哪些冲突没被自动解决。

git -C "$repo" rerere remaining

它适合放在继续 merge 或 rebase 前扫一眼:没有输出只代表 rerere 没留下待处理路径,最终还是要跑 git status --short、检查暂存差异和项目测试。环境同正文的 Git 2.39+。