bigywater.tech
Git · 52

rerere:记住冲突的解法

rerere 让 git 记住你解决过的冲突,下次遇到同样的冲突自动套用。这篇看它在 .git/rr-cache 里记了什么、冲突指纹是怎么算出来的、哪些命令能触发它,以及如何让它忘掉一个错误的解法。

c5c2066e…
rr-cache 里的目录名,等于排序后的 ours 与 theirs 两段冲突内容(各加 \0)的 SHA-1
不自动 add
自动套用解法后文件仍是 UU 状态,要 git add;rerere.autoUpdate 可以改变
merge / rebase / cherry-pick
三种命令里同样的冲突都被识别

没有 rerere 时

实测 · 本机执行输出
  $ git status --short; ls .git | grep -c rr-cache | sed "s/^ */rr-cache 目录数: /"
  UU f.txt
  rr-cache 目录数: 0
  $ git merge theirs 2>&1 | tail -2; git status --short; sed -n 4,8p f.txt
  CONFLICT (content): Merge conflict in f.txt
  Automatic merge failed; fix conflicts and then commit the result.
  UU f.txt
  <<<<<<< HEAD
  第 4 行 ours
  =======
  第 4 行 theirs
  >>>>>>> theirs

同一个冲突出现几次就要手工解决几次:合并解决后把它撤销再重做,冲突标记原样回来,工作区里没有任何记忆。

记录

实测 · 本机执行输出
  $ git merge theirs 2>&1 | grep -E "rerere|Recorded|CONFLICT"
  CONFLICT (content): Merge conflict in f.txt
  Recorded preimage for 'f.txt'
  $ ls .git/rr-cache | sed -E "s/^[0-9a-f]{40}$/<冲突指纹 40 位>/"; ls .git/rr-cache/*/ | grep -v "^$"
  <冲突指纹 40 位>
  preimage
  $ git add f.txt; git commit -q -m 第一次合并 2>&1; ls .git/rr-cache/*/ | grep -v "^$" | tr "\n" " "; echo
  Recorded resolution for 'f.txt'.
  postimage preimage
  (提交时 git 调用 rerere 记录了 postimage)

rerere.enabled=true 后,冲突发生时 git 把带冲突标记的文件存成 preimage(输出 Recorded preimage),你解决并提交时把解决后的文件存成 postimage(Recorded resolution),放在 .git/rr-cache/<指纹>/。

指纹

实测 · 本机执行输出
  preimage:
    | <<<<<<<
    | 第 4 行 ours
    | =======
    | 第 4 行 theirs
    | >>>>>>>
  postimage 的第 4 行: 第 4 行 我的解法(合并了两边)
  目录名(冲突指纹): c5c2066ea22b…   自己用 SHA-1(排序后的两段冲突内容各加 \0)算: c5c2066ea22b…   相同: True

目录名是冲突内容的 SHA-1:取冲突块里 ours 和 theirs 两段,排序后各加 \0 再算。排序使指纹与谁是 ours、谁是 theirs 无关,也不含文件名与分支名。

自动套用

实测 · 本机执行输出
  $ git merge theirs 2>&1 | grep -E "Resolved|CONFLICT|rerere"; git status --short; sed -n 4p f.txt
  CONFLICT (content): Merge conflict in f.txt
  Resolved 'f.txt' using previous resolution.
  UU f.txt
  第 4 行 我的解法(合并了两边)
  git diff 显示相对 stage 的变化(文件已按上次解法写好,但没有自动 git add,因为 rerere.autoUpdate 默认关闭):
  $ git rerere status; git diff --stat | tail -1
   1 file changed, 1 insertion(+), 1 deletion(-)

同样的冲突再出现时,git 把 postimage 的解法写进工作区文件,提示 Resolved … using previous resolution。默认不自动 git add,你检查后自己 add;设置 rerere.autoUpdate 可自动 add。

适用范围

实测 · 本机执行输出
  $ git cherry-pick theirs 2>&1 | grep -E "Resolved|CONFLICT|rerere|Recorded"; sed -n 4p f.txt
  CONFLICT (content): Merge conflict in f.txt
  Resolved 'f.txt' using previous resolution.
  第 4 行 我的解法(合并了两边)
  $ git rebase main 2>&1 | grep -E "Resolved|CONFLICT|Recorded|rerere|Rebasing"; sed -n 4p f.txt
  Rebasing (1/1)
  Auto-merging f.txt
  CONFLICT (content): Merge conflict in f.txt
  Resolved 'f.txt' using previous resolution.
  第 4 行 我的解法(合并了两边)

merge、rebase、cherry-pick 等凡是产生同样冲突块的操作都能复用。典型场景:长期分支反复 rebase 到变动的主线。

忘记与清理

实测 · 本机执行输出
  $ git merge theirs 2>&1 | grep -E "Resolved|CONFLICT"; ls .git/rr-cache/*/ | grep -v "^$" | tr "\n" " "; echo
  CONFLICT (content): Merge conflict in f.txt
  Resolved 'f.txt' using previous resolution.
  postimage preimage thisimage
  $ git rerere forget f.txt 2>&1; ls .git/rr-cache/*/ | grep -v "^$" | tr "\n" " "; echo "(forget 之后:只剩 preimage,postimage 没了)"
  Updated preimage for 'f.txt'
  Forgot resolution for 'f.txt'
  preimage thisimage (forget 之后:只剩 preimage,postimage 没了)
  $ git config --get gc.rerereResolved || echo "gc.rerereResolved 未设置(默认 60 天)"; git config --get gc.rerereUnresolved || echo "gc.rerereUnresolved 未设置(默认 15 天)"; git rerere gc; echo "git rerere gc 退出码 $?"
  gc.rerereResolved 未设置(默认 60 天)
  gc.rerereUnresolved 未设置(默认 15 天)
  git rerere gc 退出码 0

git rerere forget <文件> 删除当前冲突对应的已记录解法;git rerere gc 清理过期记录(文档默认:解决过的 60 天,未解决的 15 天)。

分步演示

rerere
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。冲突指纹的算法是我根据行为猜测并用 SHA-1 验证的(与 rr-cache 目录名一致),不是读源码得到的;rebase 一节先把合并 reset 掉再 rebase,以便产生同样的冲突
    • 没有验证:rerere 对多个冲突块的指纹(每块一个还是整体一个——本例只有一块);gc.rerereResolved/Unresolved 的实际过期行为;rerere.autoUpdate=true 的效果;git rerere diff、remaining 命令;跨仓库共享 rr-cache(把目录同步给同事);冲突标记样式 diff3 对指纹的影响
    京ICP备2026067502号-1京公网安备11010502063487号