Skip to content

09 · 冲突处理

冲突不是仓库坏了,是 Git 拒绝猜测「同一处两边都改了该留谁」。处理原则:用 abort 撤退,用 add 标记已解决,不要在中途 reset --hard。

什么时候会停下来

mergepull(merge 模式)、rebasecherry-pickstash apply/pop 都可能冲突。git status 会明确写:

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

或 rebase / cherry-pick 对应的 --continue / --abort

先看卡在哪种操作里:

bash
git status
# 也可看这些文件是否存在:
# .git/MERGE_HEAD          → merge
# .git/REBASE_HEAD         → rebase
# .git/CHERRY_PICK_HEAD    → cherry-pick

冲突标记怎么读

<<<<<<< HEAD
当前分支上的内容
=======
进来的那边的内容
>>>>>>> feature/login
  • <<<<<<<=======:当前 HEAD 一侧。
  • =======>>>>>>>:被合入 / 被 replay 的一侧。

你的任务是编辑成最终该留下的内容(可以是一边、也可以是两边拼起来),删掉这三行标记。

有的文件是二进制,没有标记,只能选一边或用外部工具。

标准处理步骤

bash
git status                      # 列出未合并路径
# 编辑每个冲突文件,去掉标记

git add src/login.ts            # 标记该文件已解决
git status                      # 全部 resolved 后再继续

# 三选一,看你在哪种操作里:
git commit                      # merge:有的 Git 版本可用 merge --continue
git merge --continue
git rebase --continue
git cherry-pick --continue

--continue 时若 Git 说「没有要提交的」,可能这颗补丁已经完全被另一边包含,rebase 下用 git rebase --skip

放弃这次操作

bash
git merge --abort
git rebase --abort
git cherry-pick --abort

工作区回到该操作开始前。这是后悔时的第一选择

不要在有 unmerged paths 时:

bash
git reset --hard                # 会丢掉已手工解决的部分,状态也更难读

除非你已决定完全放弃,并且能接受回到操作前的 HEAD(效果类似 abort,但 abort 更明确)。

ours / theirs:merge 和 rebase 相反

可以用命令整文件取一边(仍然要 add):

bash
git checkout --ours   src/login.ts
git checkout --theirs src/login.ts
# 新写法(Git 2.23+):
git restore --ours   --worktree --staged src/login.ts
git restore --theirs --worktree --staged src/login.ts

含义:

操作--ours--theirs
merge(当前在 main,merge featuremain(你原来的)feature(进来的)
rebase(当前在 feature,rebase 到 main上游 / 新基main正在 replay 的那颗提交(你的补丁)

rebase 时很多人按 merge 的直觉取 ours,结果把自己的功能整文件丢掉了。不确定就打开文件看标记,不要整文件 checkout。

stash apply 冲突时,ours 是当前工作区所在分支,theirs 是 stash。

只看冲突文件

bash
git diff                        # 未解决时会以合并差异格式显示
git diff --name-only --diff-filter=U

解决完却无法 continue

常见原因:

  1. 还留着 <<<<<<<add 了。git grep '<<<<<<<' 搜出来改掉。
  2. 解决后忘了 git add
  3. 编辑器又把文件改回冲突状态。
  4. rebase 停在空提交:git rebase --skip

反复出现相同冲突

bash
git config --global rerere.enabled true

Git 会记住「这类冲突你怎么解的」,下次自动复用。见 01 章

合并合完了才发现错了

已经不在 unmerged 状态:

  • 未 push:git reset --hard ORIG_HEAD(确认没有其它未提交宝贝)。
  • 已 push 的 merge:git revert -m 1 <merge_sha>

不要为了「清冲突」重 clone。冲突文件都在工作区,abort 就能干净。


上一章:08 · stash / worktree · 下一章:10 · 标签与发布