多数「Git 合并冲突解决不了」的情况,问题不在 Git 的合并算法,而在于没先分清冲突停在哪一层:是工作区文件里还留着冲突标记,是索引里还有未合并的 stage 条目,是本地未提交改动挡住了 merge,还是 .gitattributes、行尾符这类配置让整份文件都变成冲突。先定位层,再动手改,能避免在一个位置反复改、换个位置又失败。
判断顺序建议是:先跑git status和git ls-files -u看索引里是否还有未合并条目,再用grep -rn '^<<<<<<<' .确认工作区是否残留冲突标记,最后查git config与.gitattributes。索引干净、标记清零后再提交才算解决;未提交前可用git merge --abort或git reset --hard ORIG_HEAD回退。二进制文件、子模块、重命名冲突通常不能靠编辑器逐行改,需要选边或git rm处理。
Git合并冲突先定位在哪一层
Git 的冲突状态是分层的,改错层就会出现「明明改好了还是提交不了」。按下面四层检查,每层都有对应的观察位置和判断依据。
- 工作区文件层:文件内容里是否还有
<<<<<<<、=======、>>>>>>>三组标记。检查位置是文件本身,判断命令是grep -rn '^<<<<<<<\|^=======\|^>>>>>>>' . --exclude-dir=.git。有输出说明这一层没完成。 - 索引层:文件是否仍处于 unmerged 状态。检查命令
git ls-files -u,正常应无输出;有输出时每个文件会列出 stage 1/2/3 三个版本,分别对应共同祖先、当前分支、被合并分支。 - 提交与分支层:合并基点是否选错,比如把两个长期漂移的分支直接 merge。检查命令
git merge-base HEAD MERGE_HEAD和git log --oneline --left-right HEAD...MERGE_HEAD,看两侧差异是否远大于预期。 - 本地配置层:行尾符、合并驱动、属性规则。检查位置是
git config --list --show-origin | grep -Ei 'autocrlf|eol|merge'与仓库根的.gitattributes。若整份文件全红全绿、看不到局部差异,基本落在这一层。
Git合并冲突:先看这四个位置
四个位置按顺序看一遍,通常就能判断卡点,不用靠猜。
- 命令输出:先看完整
git status,注意both modified、both deleted、added by us、deleted by them这些状态词,它们决定后面是用编辑器改、用git rm还是git add。再加一条git diff --name-only --diff-filter=U,只列未合并文件,便于确认清单是否为空。 - 日志:
git log --merge -p -- path/to/file只显示造成冲突的那几个提交;git reflog -20看最近是否 abort、reset、cherry-pick 反复操作过;git log --oneline --graph --decorate -20确认当前是不是处在一个 merge 进行中。 - 配置项:
git config --list --show-origin | grep -Ei 'merge|autocrlf|eol|rerere',重点看core.autocrlf、merge.renormalize、rerere.enabled和自定义merge.<driver>.driver。同时打开.gitattributes与.git/info/attributes,确认没有把某类文件标记成binary或指定了不存在的合并驱动。 - 面板字段:GitHub、GitLab 的 PR 页面看「This branch has conflicts that must be resolved」以及下方列出的冲突文件;VS Code 源代码管理面板看「Merge Changes」分组,编辑器里看 Current Change / Incoming Change 两个按钮归属。面板只是视图,最终以
git status与git ls-files -u为准,两者不一致时先修命令行状态。
Git合并冲突:按可能性从高到低排除
下面按常见程度排序,每条都给一个能确认或排除的动作。
- 冲突标记没清干净,或一个文件只改了第一处。动作:
grep -rn '^<<<<<<<\|^=======\|^>>>>>>>' . --exclude-dir=.git,有输出就逐个文件处理,长文件里冲突块往往不止一处。 - 改完没
git add,索引仍是 unmerged。动作:git ls-files -u有输出说明还没登记解决结果,执行git add <file>后再查一次,直到输出为空。 - 本地未提交改动挡住合并,报错类似「Your local changes to the following files would be overwritten by merge」。动作:
git status --porcelain看有哪些改动,先 commit 或git stash push -u -m before-merge,再重新 merge。 - 行尾符或属性规则导致整文件冲突。动作:
git diff --ignore-cr-at-eol或git diff --ignore-all-space对比,若差异消失,说明是 CRLF/LF 或空白问题,需要统一.gitattributes或core.autocrlf后再处理,不要手动逐行点选。 - 重命名、删除、目录与文件同名冲突。动作:
git status里出现renamed、deleted by us等状态时,用git rm <path>或git mv <old> <new>在索引层解决,编辑器逐行合并没有意义。 - rerere 自动重放了旧的解决方案。动作:
git config --get rerere.enabled为 true 时,检查git rerere status与git rerere diff,确认自动结果是否仍符合当前上下文,不符合就改完再git add。 - 二进制文件、子模块、外部合并工具未保存。动作:二进制用
git checkout --ours <file>或--theirs整份选边;子模块用git submodule status确认指针后重新git add;用git mergetool时要确认工具确实保存并退出,否则文件内容没变。
一个容易踩的坑:在冲突未解决完时直接 git commit -a 或点 IDE 的「全部接受当前更改」,可能把对方整段改动丢掉,而且提交历史里看不出来。git checkout --ours 对新增文件、删除文件的表现也和普通修改不同,用它之前先用 git status 确认冲突类型。

Git合并冲突:改完怎么确认真的解决了
不要只看编辑器里标记消失就推送,按下面顺序验证一遍。
git ls-files -u # 期望:无输出,索引里没有 unmerged 条目
grep -rn '^<<<<<<<\|^=======\|^>>>>>>>' . --exclude-dir=.git # 期望:无输出
git diff --check # 期望:无输出,没有残留冲突标记和空白错误
git status # 期望:working tree clean,或只剩你明确要提交的改动
git add <file> && git commit # 提交合并结果
git cat-file -p HEAD | grep -c parent # 期望:2,说明是合并提交
提交后还需要确认内容没被改错:用 git show --stat HEAD 看本次合并涉及的文件范围是否符合预期,用 git log --oneline --graph -10 确认历史形状正常。之后跑一遍仓库里已有的构建或测试命令(例如项目自带脚本),本地再实际改动一处相关文件试一次,确认能正常迭代。观察多久算稳定没有统一标准,建议至少等到这次改动经过一次完整构建、并完成一次常规代码评审或 CI 流程后再视为稳定,遇到构建失败仍按回滚处理。
回滚要分两种情况。合并提交还没推送时,最省事的是 git merge --abort(merge 仍在进行中),已经提交则用 git reset --hard ORIG_HEAD 回到合并前,或用 git reset --soft ORIG_HEAD 保留改动在工作区。已经推送的合并提交不要用 reset 强推,改用 git revert -m 1 <merge-commit> 生成一个反向提交;其中 -m 1 表示保留第一父提交(通常是目标分支)的内容,恢复后需要重新走一次评审。