Git 冲突怎么解决:别只删标记,先看懂暂存区的 1、2、3 阶段

63 阅读3分钟

Git 冲突怎么解决:别只删标记,先看懂暂存区的 1、2、3 阶段

Git 合并冲突的分支汇合示意图

Git 冲突最容易让人着急的,是工作区里那几行 <<<<<<<=======>>>>>>>

我现在遇到冲突,反而会先停一下。那三段文字只是 Git 给你的待编辑结果,真正有用的信息还在暂存区里。

拿一个很小的例子说。当前分支和待合入分支都改了 config.yml 的同一行:

git merge feature
# CONFLICT (content): Merge conflict in config.yml

这时先看状态:

git status --short
# UU config.yml

git ls-files -u

UU 表示这个路径在两个方向都没有自动合上。后一个命令会列出同一路径的三条记录,末尾的数字就是 stage:

100644 <blob> 1  config.yml
100644 <blob> 2  config.yml
100644 <blob> 3  config.yml

Git 合并冲突期间暂存区的三个阶段:共同祖先、当前分支和待合入分支

这三个版本不是抽象概念,直接能拿出来看:

git show :1:config.yml  # 两个分支分开前的共同祖先
git show :2:config.yml  # 当前分支 HEAD,也就是 merge 的当前侧
git show :3:config.yml  # 正在合入的那个分支

我觉得这比只盯着冲突标记靠谱得多。比如配置里的端口和运行模式都被改了,先看共同祖先,才知道每一边究竟改了哪一项。否则手动拼出来一个“看着都保留了”的文件,可能已经把本来互斥的配置混在一起。

确认目标内容后,编辑工作区里的 config.yml,把冲突标记删干净。然后不是直接 commit,而是先把它标记为已解决:

git add config.yml
git merge --continue

git add 在这里的意思不是“暂存一点改动”,而是告诉 Git:这个路径不再保留三份未合并版本,工作区当前这份就是最终结果。

有个常见误区是冲突一出现就跑 git reset --hard。如果只是这次 merge 不想要了,语义更准确的是:

git merge --abort

Git 官方手册也提醒过,merge 开始前如果工作区就有未提交改动,abort 不一定能把所有现场完整还原。所以准备合分支前先把自己的改动提交或临时存起来,这一步真能少掉很多后悔。

最后说一个不该被神化的小工具。长生命周期分支反复和主干对齐时,同一种冲突确实会反复出现,可以开启 rerere:

git config rerere.enabled true

它会记录第一次冲突的自动合并结果和你手工修好的结果。下一次再次出现同样的冲突前像时,Git 可以把之前的解析应用到工作区。

但这不是“以后不用看冲突了”。复用出来的结果仍然要看 diff、跑测试,再 git add。同一段文本冲突,业务含义可能已经变了。

本文命令语义按 Git 2.47.3 的 git-mergegit-rerere 官方手册核对。