Git合并冲突怎么解决?

文章导读
合并冲突卡住时,先不要直接打开文件改 <<<<<<< 标记。先用 git status 看清哪些路径处于未合并状态,再判断当前处在 merge、rebase 还是 cherry-pick:这三种操作里 ours 和 theirs 指向的分支不一样,版本取反比改错代码更难回退。多数“改完又冲突”的情况,原因在于选错了要保留的一方,或者把锁文件、生成文件当源码
📋 目录
  1. Ⅰ Git合并冲突先定位在哪一层
  2. Ⅱ Git合并冲突:先看这四个位置
  3. Ⅲ Git合并冲突:按可能性从高到低排除
  4. Ⅳ Git合并冲突:改完怎么确认真的解决了
A A

合并冲突卡住时,先不要直接打开文件改 <<<<<<< 标记。先用 git status 看清哪些路径处于未合并状态,再判断当前处在 merge、rebase 还是 cherry-pick:这三种操作里 ours 和 theirs 指向的分支不一样,版本取反比改错代码更难回退。多数“改完又冲突”的情况,原因在于选错了要保留的一方,或者把锁文件、生成文件当源码手动合并。

Git 冲突要先分层定位:工作区的冲突标记、索引里 stage 1/2/3 的条目、尚未完成的合并提交、以及 .gitattributes 和 merge driver 配置。建议先用 git diff --name-only --diff-filter=U 列出未合并路径,逐条确认成因后再改;改完用 git diff --check 加一次完整构建验证,提交前保留 git merge --abort 这条回头路。

Git合并冲突先定位在哪一层

冲突不是一处,是四层。改错层的典型表现是:文件里标记删干净了,git status 还提示未合并;或者提交完成后发现整个文件被替换成了对方的版本。按下面四层依次确认,能避免在同一处反复折腾。

  • 工作区层:文件里出现 <<<<<<< HEAD 与 >>>>>>> 标记。检查位置是 git diff -- path/to/file 输出的冲突块。
  • 索引层:git ls-files -u 是否列出 stage 1(base)、2(ours)、3(theirs)条目。只要有输出,就说明该路径还没 git add。
  • 提交层:git status 首行显示 “You have unmerged paths” 或 “All conflicts fixed but you are still merging”;ls .git/MERGE_HEAD 存在说明在合并中,.git/rebase-merge 存在说明在 rebase 中。判断依据不同,退出命令也不同。
  • 配置层:git check-attr -a path/to/file 看 .gitattributes 是否给该文件指定了 merge 策略,git config --get merge.conflictstyle 看你能否看到 base 段。

Git合并冲突:先看这四个位置

下面这段可以直接复制到仓库根目录执行,用来把状态、未合并路径和冲突成因一次看清;把示例路径换成实际文件即可。

# 1. 当前处于什么状态(merge / rebase / cherry-pick)
git status -sb
ls .git/MERGE_HEAD .git/rebase-merge .git/rebase-apply 2>/dev/null

# 2. 哪些路径还没合并完
git diff --name-only --diff-filter=U
git ls-files -u

# 3. 让冲突块带上 base 段,便于判断改了什么
git config merge.conflictstyle zdiff3

# 4. 只看引入冲突的提交
git log --merge -p -- path/to/file
  1. 日志:git reflog -20 找回冲突前的 HEAD;git log --merge -p -- path/to/file 只看冲突双方的提交差异,比翻全量历史快。
  2. 命令输出:git status -sb 的短格式里,UU 表示双方都改、AA 表示双方都新增、DU/UD 表示一边删除一边修改。这两个字母分别对应 ours 和 theirs。
  3. 配置项:git config --get merge.conflictstyle、git config --get core.autocrlf、git check-attr -a path/to/file。行尾和 merge 策略引起的“整文件冲突”基本都在这三项里露头。
  4. 面板字段:IDE 的 Source Control 面板里,“Current (ours)” 与 “Incoming (theirs)”、Merge Editor 顶部的分支名、文件列表里的 U 标记。面板只反映索引状态,判断仍需回到 git status 核对。

Git合并冲突:按可能性从高到低排除

按出现频率往下排,每条给出确认动作,确认不了就不要动手改。

Git合并冲突怎么解决?
  1. 同一区域被双方修改(最常见)。确认:git diff -- path/to/file 看冲突块两侧是否都含实质逻辑。处理:保留正确逻辑,删掉三行标记和 base 段,再 git add path/to/file。
  2. ours / theirs 记反,在 rebase 和 cherry-pick 中尤其容易。确认:git status 首行是 “rebase in progress” 还是 “You have unmerged paths”;rebase 时 ours 是上游基底,theirs 才是你的提交,和 merge 相反。处理:不要用 git checkout --theirs . 批量覆盖,逐个文件比对后再 git add。
  3. 行尾或编码差异导致整文件冲突。确认:git diff --stat 显示几乎整个文件都在冲突,git diff --ignore-all-space 后差异大幅减少,file path/to/file 看编码是否一致。处理:统一 .gitattributes(例如 * text=auto),不要靠手动改几千行。
  4. 锁文件与生成产物冲突,如 package-lock.json、yarn.lock、构建输出目录。确认:git status 里出现这类路径。处理:取一边后重新执行安装或构建生成,再 git add,不要手写合并。
  5. 重命名或删除与修改相撞。确认:git status 显示 “deleted by us / added by them”。处理:先确认该文件是否还需要,需要就 git add 保留,不需要就 git rm。

这里最容易踩的坑是把 git checkout --ours <file> 当成“保留我的改动”的万能命令。它在 merge 里确实指当前分支,但在 rebase 里指上游基底,执行后你的提交内容会被整体丢弃,而且这一步不进 reflog 之外的历史。批量覆盖前先 git stash push -u 或建一个备份分支。

Git合并冲突:改完怎么确认真的解决了

验证分三步,都在仓库根目录执行:

Git合并冲突怎么解决?
# 1. 是否还有冲突标记残留或空白错误
git diff --check
git grep -n '^<<<<<<< '

# 2. 索引里是否还有未合并条目
git status
git ls-files -u

# 3. 提交后确认是合并提交(应输出两个父提交哈希)
git show --pretty=%P -s HEAD

git diff --check 无输出、git grep 搜不到标记、git ls-files -u 为空,才算索引干净;git status 应停在 “All conflicts fixed but you are still merging”,此时再 git commit 完成合并。

文件能打开不等于冲突解决了。建议合并提交后跑一次完整构建和与该文件相关的测试用例,确认行为符合预期;合入主分支后再看一次 CI 流水线是否通过。观察时长按项目节奏定,通常至少覆盖一次完整构建加一次代码评审,不要只看本地能否启动。

回滚要按阶段选命令,并且先保住现场:

  • 还没提交:merge 用 git merge --abort,rebase 用 git rebase --abort,回到冲突前状态。
  • 已提交但没推送:先 git branch backup-merge 留底,再 git reset --hard ORIG_HEAD。这条命令会丢弃工作区未提交改动,执行前确认没有要留的内容。
  • 已经推送:用 git revert -m 1 <merge-commit> 生成反向提交,不要 force push 改写公共历史。后续若要重新合入同一分支,需要先 revert 掉这次 revert,否则该分支的改动不会再进来。