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