Git分支合并冲突怎么解决

文章导读
搜「Git分支合并冲突怎么解决」的人,多数不是不知道冲突是什么,而是已经卡在某一步:文件里全是 <<<<<<< 标记不敢动,或者改完了 git status 还显示一堆 unmerged,或者 merge commit 提交完发现代码跑不起来。处理顺序建议是:先用 git status -sb 和 git diff --name-only --diff-f
📋 目录
  1. A Git分支合并冲突先定位在哪一层
  2. B Git分支合并冲突:先看这四个位置
  3. C Git分支合并冲突:按可能性从高到低排除
  4. D Git分支合并冲突:改完怎么确认真的解决了
A A

搜「Git分支合并冲突怎么解决」的人,多数不是不知道冲突是什么,而是已经卡在某一步:文件里全是 <<<<<<< 标记不敢动,或者改完了 git status 还显示一堆 unmerged,或者 merge commit 提交完发现代码跑不起来。处理顺序建议是:先用 git status -sb 和 git diff --name-only --diff-filter=U 确认冲突停在哪一层,再决定是编辑文件、执行 git add,还是收尾提交或整体回滚。不要一上来就手工删标记,也不要习惯性 git merge --abort 把别人的改动一起丢掉。

合并冲突的定位优先级:先看 git status 里的 unmerged paths 和冲突类型(content / modify-delete / rename / binary),再判断是内容冲突还是行尾符、大小写、编码这类环境差异。内容冲突逐块比对两侧改动后再 git add;环境差异先统一 .gitattributes 与 core.autocrlf 再重新归一化。未提交前用 git merge --abort 回滚最安全,已提交未推送用 git reset --hard ORIG_HEAD,已推送则只能 revert 合并提交。以上操作都不会替你判断业务语义,冲突块取舍仍需人工确认。

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

冲突不是一个状态,而是四个阶段。位置判断错了,后面所有操作都会事倍功半。

  • 工作区层:文件里出现 <<<<<<< HEAD 这类标记。判断依据是直接打开冲突文件能看到分隔符。这一层只需要编辑文件。
  • 暂存区层:文件已改好但没 git add,git status 仍把它列在 “Unmerged paths” 或 “both modified”。判断依据是 git ls-files -u 输出非空。这一层只需要 git add 把冲突条目标记为已解决。
  • 提交层:所有冲突文件都 add 完了,但合并还没结束,.git/MERGE_HEAD 仍然存在。判断依据是 git rev-parse -q --verify MERGE_HEAD 有输出。这一层只需要 git commit 收尾。
  • 环境层:改完提交了,但 diff 里全是整段整段的“假冲突”,或者两个文件看起来一样却仍报冲突。判断依据是 git diff --stat 显示整个文件重写、git diff --check 报 whitespace 问题。这一层要回到 .gitattributes、core.autocrlf、core.ignorecase 上查。

先用下面这几条命令把层次定下来,再动手,避免在错误的层反复折腾:

# 当前分支状态与是否处于合并中
git status -sb
git rev-parse -q --verify MERGE_HEAD

# 只列出未合并(冲突)文件
git diff --name-only --diff-filter=U
git ls-files -u

# 是否存在空白/行尾符类问题
git diff --check

# 两侧分叉情况
git log --oneline --graph --left-right HEAD...MERGE_HEAD | head -20

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

这四个位置按“能看到什么”排序,前两个定性质,后两个定成因。

Git分支合并冲突怎么解决
  1. 合并命令的原始输出。重新执行一次 git merge <branch>(已处于合并中会提示 “You have not concluded your merge”),或翻看终端回滚记录,找 CONFLICT (content): Merge conflict in path/to/file 这一行。括号里的类型是关键:content 是内容冲突,modify/delete 是一侧删一侧改,rename/rename 是两边都改名,binary 是二进制文件。
  2. git status 的详细输出。执行 git status,看 “Unmerged paths” 段落里每个文件前的状态码,UU 表示两侧都改、DU/UD 表示一侧删除、AA 表示两侧都新增。完整路径直接抄进后续命令,不要用通配符。
  3. 仓库配置项。逐个查:git config --get core.autocrlf、git config --get core.ignorecase、git config --get merge.conflictstyle、git config --get merge.renormalize;再看仓库根目录 .gitattributes 是否存在、是否声明了 * text=auto。四项里任何一项在两端机器不一致,都会制造大量假冲突。
  4. 托管平台与编辑器面板。GitHub 的 PR 页面在 Conversation 下方会显示 “This branch has conflicts that must be resolved”,并可点 “Resolve conflicts” 在线处理;GitLab 的 MR 页面在 “Overview” 顶部显示同样提示,冲突文件在 Changes 标签里带 conflict 标记。本地 IDE(VS Code 的 Source Control 面板、IntelliJ 的 Merge Conflicts 节点)会列出每个冲突块,并给出 Accept Current / Accept Incoming 按钮——这几个按钮等价于取一边,风险见下一节。

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

下面按实际遇到频率排序,每条都给出确认动作和对应处理。

  • 同一文件同一区域被两侧修改(最常见)。确认:git diff -- path/to/file 能看到成对的 -/+ 块。处理:打开文件逐个冲突块保留语义正确的行,删掉 <<<<<<<、=======、>>>>>>> 三行标记,再 git add path/to/file。
  • 行尾符或空白差异造成的假冲突。确认:git diff --ignore-all-space -- path/to/file 后差异明显变小,或 git diff --check 报 trailing whitespace。处理:在仓库根加 .gitattributes 写 * text=auto eol=lf,然后 git add --renormalize . 并单独提交一次归一化改动,不要混在合并提交里。
  • 一侧删除、一侧修改(modify/delete)。确认:git status 里该文件状态码为 DU 或 UD。处理:决定保留就 git add path/to/file,决定删除就 git rm path/to/file,两者都在冲突解决阶段执行。
  • 两侧都重命名或重命名后修改。确认:git status 显示 rename/rename 或同一内容出现在两个新路径。处理:与改动方确认最终文件名,git rm 掉不要的那一份,保留的 git add。
  • 二进制文件冲突。确认:git diff --numstat -- path 两个数字位显示为 -。处理:只能二选一,git checkout --ours -- path 或 git checkout --theirs -- path 后 git add;需要合并内容时只能人工用外部工具比对。
  • 大小写或编码差异(macOS / Windows 常见)。确认:git ls-files | grep -i 文件名 出现两条只差大小写的记录。处理:两侧统一文件名大小写后提交,必要时调整 core.ignorecase,此项修改前建议先备份工作区。

一个高频坑:git checkout --ours / --theirs 的含义在 merge 和 rebase 下是反的。merge 时 ours 是当前分支、theirs 是被合入分支;rebase 时 ours 是上游基线、theirs 是你在重放的提交。另外这两个命令是整文件覆盖,不是逐块取舍,用它“快速解决”很容易把另一侧同一文件里的其他改动一起丢掉。

Git分支合并冲突怎么解决

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

解决冲突后的状态检查,建议按下面顺序执行,全部通过再提交:

# 1. 不再有未合并条目(应无输出)
git ls-files -u
git diff --name-only --diff-filter=U

# 2. 不再有冲突标记残留
git diff --check
git grep -n -E '^(<<<<<<< |=======$|>>>>>>> )' -- .

# 3. 合并可收尾
git rev-parse -q --verify MERGE_HEAD   # 有输出说明待 commit
git commit                             # 沿用默认合并信息,或 -m 补充说明

# 4. 提交后确认工作区干净
git status -sb
git log --oneline -3

命令层面通过只说明索引干净,不代表代码正确。还要跑一次项目自带的构建与测试命令(例如前端 npm ci && npm test、Java 项目 mvn -q test、Go 项目 go build ./... && go test ./...,以仓库里的脚本为准),并对被冲突的文件所涉及的功能路径做一次手工点验。观察多久算稳定:本地完整构建通过之后,推送到远端看第一轮 CI 结果;合并进主干后,通常建议至少观察一个正常的发布或迭代周期内没有由本次改动引发的新报错,再认为这次冲突处理是干净的——具体周期需要结合团队的发布节奏确认,没有统一标准。

回滚按“提交到哪一步”分三种,不要混用:

  • 还没 git commit:git merge --abort,回到合并前状态。此命令只在 MERGE_HEAD 存在时有效,提交之后就失效了。
  • 已提交但没推送:git reset --hard ORIG_HEAD。执行前先用 git log --oneline -3 确认 ORIG_HEAD 指向的确实是合并前的位置;--hard 会丢弃工作区改动,未提交的内容先 git stash。
  • 已推送到远端共享分支:不要 reset 后强推,用 git revert -m 1 <merge-commit-sha> 生成一个反向提交,-m 1 表示保留第一父提交(通常是主干)那条线。revert 合并提交后再想重新合并同一分支,需要先 revert 掉这次 revert,否则之前的改动不会再次进来。