Git rerere 复用冲突解决:文件已改好,为什么仍要检查暂存区
长期维护一个主题分支时,同一处冲突可能在试合并、撤销和再次合并中反复出现。Git rerere 可以记住此前如何把冲突内容改成解决后的内容,在再次遇到匹配冲突时尝试复用。这能省去机械编辑,但历史解法是否符合本次业务要求,仍需要人和测试判断。
先在一次性仓库学会一个解法
下面脚本适用于 Bash,已在 Git 2.52.0、Bash 5.2.37 下运行。它只创建并删除自己的临时目录,使用虚构提交身份,配置限于临时仓库;不要删去隔离步骤后粘到真实项目。两条分支分别把同一行改成 left 和 right,故意制造可重复冲突。
AI概念示意图:用抽象图形说明本文主题,不代表真实界面或运行结果。
#!/usr/bin/env bash set -euo pipefail export LC_ALL=C GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null lab=$(mktemp -d) trap 'rm -rf -- "$lab"' EXIT git -c init.templateDir= init -q -b main "$lab/repo" cd "$lab/repo" git config user.name 'Demo' git config user.email 'demo@example.invalid' git config commit.gpgSign false git config rerere.enabled true git config rerere.autoupdate false printf 'base\n' > choice.txt git add choice.txt git commit -qm base git branch side printf 'left\n' > choice.txt git commit -qam left git switch -q side printf 'right\n' > choice.txt git commit -qam right git switch -q main if git merge --no-edit side; then exit 1; fi [[ -n $(git ls-files -u) ]] printf 'chosen\n' > choice.txt git rerere git merge --abort if git merge --no-edit side; then exit 1; fi [[ $(cat choice.txt) == chosen ]] [[ -n $(git ls-files -u) ]] printf 'worktree=chosen index=unmerged\n' git diff -- choice.txt git add choice.txt [[ -z $(git ls-files -u) ]] git commit -qm 'resolve with reviewed result' [[ $(git show HEAD:choice.txt) == chosen ]] printf 'committed=chosen\n'
第一次记录输入与解决后的内容
第一次 merge 预期失败,脚本用 if 接住非零状态,并断言暂存区确有未合并条目。随后把文件改为 chosen,再执行 git rerere,让 Git 记录已解决的内容。此时不提交合并,而是中止;工作树回到合并之前,但刚学会的解决记录仍可供下一次尝试使用。
显式运行 rerere 是为了把教学步骤拆开。启用该功能后,一些合并和提交命令也会在适当时机调用它。记录不是另一份普通业务文件,也不会因为提交项目文件就自动变成可共享的团队配置;不要假定同事克隆仓库后自然拥有同一份记忆。
第二次复用,并没有完成整个合并
第二次 merge 再遇到相同冲突,工作区文件变成 chosen,但脚本仍断言 git ls-files -u 非空。这里明确关闭 rerere.autoupdate,让自动复用停在工作区,暂存区继续保留冲突阶段,给人工审查留出清晰位置。最终输出 worktree=chosen index=unmerged 就是这两层状态的对照。
检查内容并通过相关测试后,才执行 git add;随后未合并条目应当清空,再正常完成合并提交。脚本最后输出 committed=chosen。仅看到文件里的冲突标记消失,不能证明暂存区已经解决,也不能证明合并已经产生提交。
旧解法只是候选结果
真实项目里,复用完成后先看 git diff 和合并状态,再运行覆盖冲突含义的测试。例如重复修改的是权限判断,应覆盖允许和拒绝两条路径;修改的是配置优先级,应验证两边新增加的条件。只检查语法通过,很容易保留一个已经过时的业务决定。
如果当前冲突的旧解法已经不合适,可以在对应冲突状态下使用 git rerere forget 路径,忘掉该冲突的已记录解决方式,再重新处理。不要为了一个错误结果就盲目删除整个缓存;清理范围应与要纠正的历史解法一致。
rerere 依据冲突内容识别可复用关系,并不理解需求。文件大幅重构、冲突形态变化时,它也未必找到匹配;反过来,即使找到匹配,周边代码的意义仍可能变化。把它放在“自动提出编辑结果、人工验收再暂存”的流程里,作用和边界就比较清楚。
本实验没有模拟远程推送、多人共享或所有类型的冲突,主要验证普通文本行冲突的学习与复用。迁移到项目时,可先在自己的仓库局部启用并观察几次,确认团队如何检查和提交解决结果,再讨论是否扩大使用范围。


