Git冲突原因与解决方法全解

Git 代码冲突:全部原因 + 解决方法(附沙箱实测)

本文所有报错信息、冲突文件内容、命令输出均为 Git 2.43 真实运行截图 ,不是凭记忆写的。

配套可复现脚本:git-conflict-lab/demo.shbash demo.sh 一键重跑全部场景)。


一、先搞懂:Git 到底为什么会冲突

一句话原理

Git 的合并基于三路合并(three-way merge)

复制代码
        base(共同祖先版本)
         /              \
    ours(你的)      theirs(对方的)
         \              /
          合并结果

Git 的判断逻辑只有三条:

情况 谁改了 结果
只有一边改了这一行 ours 或 theirs ✅ 自动采用改动的那边
两边都没改 都不 ✅ 保持原样
两边都改了同一块 ours theirs 冲突,必须人来裁决
一边改、一边删 冲突类型不同 冲突(modify/delete)

关键认知 :Git 冲突的本质不是"Git 不行",而是 Git 无法替你判断"到底该听谁的"

所以解决冲突不是技术活,是决策活------你要读懂两边的意图,然后做决定。

冲突文件长什么样(实测输出)

复制代码
public class OrderService {
    public void cancel(Long id) {
<<<<<<< HEAD
        System.out.println("【master】取消订单并释放库存");
=======
        System.out.println("【feature】超时未支付自动取消,发短信通知");
>>>>>>> feature/order-cancel
    }
}

三个标记,记住它们

标记 含义
<<<<<<< HEAD 冲突开始,HEAD 后面是当前分支的内容
======= 分界线
>>>>>>> feature/order-cancel 冲突结束,下面是被合并进来的内容

⚠️ 7 个字符,一个都不能少<<<<<<< 是 7 个 <>>>>>>> 是 7 个 >======= 是 7 个 =

(很多人手写时少写一个导致文件损坏。)


二、冲突的 16 种原因(按类型分类)

A 类:内容冲突(代码本身撞车)

1️⃣ 两边改了同一文件的同一行 / 相邻行 ------ 占所有冲突的 90%

报错原文(实测)

复制代码
$ git merge feature/order-cancel
Auto-merging OrderService.java
CONFLICT (content): Merge conflict in OrderService.java
Automatic merge failed; fix conflicts and then commit the result.

触发原因:两个分支基于同一个提交,改了同一段代码。

⚠️ 即使是相邻行 (比如你改第 10 行,对方改第 11 行)也可能冲突,

因为 Git 的合并单位是"hunk"(上下文相关的代码块,默认上下各 3 行),不是严格的行。


2️⃣ 一方修改、一方删除(modify/delete)

报错原文(实测)

复制代码
$ git merge feature/clean
CONFLICT (modify/delete): LegacyUtil.java deleted in feature/clean and modified in HEAD.
Version HEAD of LegacyUtil.java left in tree.
Automatic merge failed; fix conflicts and then commit the result.

$ git status
Unmerged paths:
  (use "git add/rm <file>..." as appropriate to mark resolution)
	deleted by them: LegacyUtil.java

决策点:这个文件到底该留还是该删?

  • 留 → 说明对方的删除是错的,或者你这个文件还有用
  • 删 → 说明你的改动白做了,应该跟着删

3️⃣ 二进制文件两边都改(png/jar/xlsx/docx)

报错原文(实测)

复制代码
$ git merge feature
Auto-merging logo.png
CONFLICT (content): Merge conflict in logo.png

为什么特殊 :Git 无法对二进制做文本 diff,所以不会产生 <<<<<<< 标记,只能二选一。

bash 复制代码
git checkout --ours  logo.png    # 保留我的
git checkout --theirs logo.png   # 保留对方的

实测结果:--ours 后文件内容确实是 master 的 fake-C


4️⃣ 新增了同名文件(add/add)
复制代码
CONFLICT (add/add): Merge conflict in UserService.java

典型场景 :你和同事各自新建了同名文件(比如都新建了 OrderVO.java),但内容完全不同。

常见于大家同时按同一个需求文档开工。


5️⃣ 重命名冲突(rename/modify、rename/rename)
复制代码
CONFLICT (rename/modify): OrderDto.java renamed to OrderVO.java in HEAD, but modified in feature
CONFLICT (rename/rename): a.java renamed to b.java in HEAD, renamed to c.java in feature

典型场景:你重命名了类(IDEA 的 Refactor → Rename),同事同时改了这个类的内容。


B 类:操作方式导致的冲突

6️⃣ git pull 冲突(本地提交 vs 远程提交分叉)
复制代码
$ git pull
CONFLICT (content): Merge conflict in pom.xml
Automatic merge failed; fix conflicts and then commit the result.

原因git pull = git fetch + git merge。你的本地有新提交,远程也有新提交,两条线分叉了。

💡 实习生最常踩的坑git pull 之前不先 git fetch 看一眼,直接 pull 就把冲突糊脸上了。


7️⃣ git rebase 冲突 ------ 同一个冲突可能要解决 N 次

报错原文(实测)

复制代码
$ git rebase master
Rebasing (1/2)
Auto-merging cfg.txt
CONFLICT (content): Merge conflict in cfg.txt
error: could not apply 9a315ab... feat: feature 改第2行
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

为什么比 merge 痛苦 :rebase 是把你的提交逐个应用到目标分支上

如果你有 5 个提交都碰了同一处,你要解 5 次

复制代码
解决 → git add → git rebase --continue → 又冲突 → 解决 → add → continue → ...

实测最终效果:成功 rebase 后历史是一条直线

复制代码
* 02a644d feat: feature 新增第4行
* 3793fae feat: feature 改第2行
* 5c4ad3f fix: master 改第2行     ← 你的提交被"挪"到了 master 之后
* faca9f5 init

8️⃣ git cherry-pick 冲突
复制代码
$ git cherry-pick abc1234
error: could not apply abc1234... feat: xxx
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' and run "git cherry-pick --continue"

原因 :把某个提交抄到另一个分支,但那个分支的上下文已经变了。

解法 :同 rebase,git cherry-pick --continue / --abort


9️⃣ git stash pop 冲突
复制代码
$ git stash pop
CONFLICT (content): Merge conflict in OrderService.java

原因 :你 stash 了改动 → 期间别人改了同一处 → pop 回来撞车。

注意 :stash pop 冲突时,stash 不会被自动删除 ,解决完要手动 git stash drop


🔟 工作区有未提交改动,直接被拒绝(不是冲突,但很像

报错原文(实测,真实踩到的)

复制代码
$ git merge feature/clean
error: Your local changes to the following files would be overwritten by merge:
	LegacyUtil.java
Please commit your changes or stash them before you merge.
Aborting

三种选择

bash 复制代码
git commit -am "wip: 先存一下"     # ① 提交(推荐)
git stash                          # ② 暂存起来,事后 git stash pop
git checkout -- LegacyUtil.java    # ③ 丢弃(危险,改动找不回)

C 类:环境 / 配置导致的"假冲突"(最冤)

1️⃣1️⃣ 换行符不一致(CRLF vs LF)------ Windows 用户专属噩梦

现象 :明明只改了一行,却提示整个文件都冲突

原因 :Windows 用 \r\n,Linux/Mac 用 \n。你提交时把整个文件的换行符都改了。

解决

bash 复制代码
# Windows 用户全局配置(提交时转 LF,检出时转 CRLF)
git config --global core.autocrlf true

# Linux / macOS
git config --global core.autocrlf input

# 项目根目录加 .gitattributes(推荐,团队统一)
*.java text eol=lf
*.md   text eol=lf
*.png  binary

1️⃣2️⃣ IDE 自动格式化 / 自动 import 导致大面积冲突

现象:你只改了 3 行,diff 出来 200 行。

原因 :IDEA 的 Reformat CodeOptimize Imports 把同事的代码也重排了;

或者同事用了不同的 code style(缩进 2 空格 vs 4 空格)。

解决 :团队统一导入同一份 .editorconfig + IDEA code style 配置文件。

ini 复制代码
# .editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space

[*.java]
indent_size = 4

1️⃣3️⃣ 文件名大小写不一致

现象 :本地好好的,推到服务器上 CI 就冲突/报错。

原因 :Windows 和 macOS 默认不区分文件名大小写OrderVO.javaOrderVo.java 是同一个文件;

Linux 区分 ,是两个文件。

解决git config core.ignorecase false(Linux 上),并统一命名规范。


1️⃣4️⃣ 文件权限变更(mode change)
复制代码
diff --git a/deploy.sh b/deploy.sh
old mode 100644
new mode 100755

解决git config core.fileMode false(Windows 上推荐关掉,否则 chmod 会被当改动)。


1️⃣5️⃣ 子模块(submodule)冲突
复制代码
CONFLICT (submodule): Merge conflict in libs/common

原因 :两边引用了子模块的不同 commit。

解决

bash 复制代码
git checkout --ours libs/common   # 或 --theirs
git add libs/common
git submodule update --init --recursive

1️⃣6️⃣ 目录 / 文件类型冲突
复制代码
CONFLICT (file/directory): There is a directory with name config in feature.
Adding config as config~HEAD

原因 :你建了个 config 目录,同事建了个 config 文件。

解决 :手动决定保留哪个,git rm 掉另一个。


三、解决方法(从通用到专用)

🔧 通用五步法(适用于 merge / pull)

bash 复制代码
# ① 看清楚哪些文件冲突了
git status

# ② 打开冲突文件,找到 <<<<<<< ======= >>>>>>> 标记
#    手动改成你想要的最终内容(删掉所有标记!)

# ③ 标记为已解决
git add <文件>

# ④ 完成合并
git commit              # 会自动生成 Merge 提交信息
# 或 git commit -m "merge: 融合 xxx 与 xxx"

# ⑤ 验证
git log --oneline --graph --all

🚨 最容易犯的错 :改完文件忘记 git add,直接 git commit → 报 Committing is not possible because you have unmerged files

或者更糟:<<<<<<< HEAD 这些标记提交上去了,代码直接编译不过。

提交前自检

bash 复制代码
grep -rn "<<<<<<<\|>>>>>>>\|=======" --include="*.java" .
# 有输出 = 还有标记没清理干净,别提交!

🛠 三种解决策略

策略 命令 适用场景
听我的 git checkout --ours <file> 确认自己的改动是最新最对的
听对方的 git checkout --theirs <file> 对方是重构后版本,我的要废弃
融合两边 手动编辑 两边的功能都要保留(最常见、最正确)

批量处理(冲突文件很多时)

bash 复制代码
git checkout --ours   .          # 全部用我的(谨慎!)
git checkout --theirs .          # 全部用对方的(谨慎!)
git diff --name-only --diff-filter=U | xargs git checkout --ours   # 只处理冲突文件

-X 参数在合并时就指定策略(不进冲突状态,直接自动选边)

bash 复制代码
git merge -X ours   feature    # 冲突时一律用我的(其他部分照常合并)
git merge -X theirs feature    # 冲突时一律用对方的

⚠️ 高频陷阱:rebase 时 --ours/--theirs反的

这是最容易误删自己代码的地方,我做了实测验证:

复制代码
=== 在 feature 分支上执行 git rebase master ===
--- git checkout --ours   取到的内容: MASTER版本   ← 直觉以为是 feature,其实是 master!
--- git checkout --theirs 取到的内容: FEATURE版本  ← 你的代码在这边!

=== 对照:在 master 分支上执行 git merge feature ===
--- git checkout --ours   取到的内容: MASTER版本
--- git checkout --theirs 取到的内容: FEATURE版本

记忆口诀

ours = 当前 HEAD 所在的那一边,不是"我的分支"。

  • merge 时 HEAD 在你当前分支 → ours = 你的
  • rebase 时 Git 把 HEAD 切到了目标分支(master),再逐个重放你的提交 → ours = master,你的代码在 theirs

血泪教训 :rebase 冲突时想保留自己的代码,要用 --theirs,用错了就把自己的活全丢了。


🖥 图形化工具解决(Java 开发首选 IDEA)

IDEA 操作路径

  1. 冲突时 IDEA 自动弹出 Conflicts 对话框

  2. 或右键项目 → GitResolve Conflicts

  3. 双击文件,进入三栏合并界面

    ┌──────────────┬──────────────┬──────────────┐
    │ 左边 │ 中间 │ 右边 │
    │ Yours/当前 │ Result │ Theirs/传入 │
    │ (本地) │ (最终结果) │ (对方分支) │
    └──────────────┴──────────────┴──────────────┘
    ↑ >> 接受左边 ↑ 中间可直接编辑 接受右边 << ↑

  4. 也可以直接在中间的 Result 栏手打(推荐,最灵活)

  5. Apply → 文件自动 git add

  6. 全部解决完 → git commit

IDEA 快捷键 :冲突文件上按 Alt+M(Win)/ ⌥+M(Mac)快速打开合并界面。

VS Code :冲突文件会显示 Accept Current Change / Accept Incoming Change / Accept Both Changes / Compare Changes 四个按钮,一键解决。

命令行党的 mergetool

bash 复制代码
git mergetool          # 调用配置的外部工具(vimdiff / beyond compare / meld)
git config --global merge.tool vimdiff

🔁 rebase 冲突专用流程

bash 复制代码
git rebase master

# 冲突了 ↓
git status                    # 看哪个文件
# 手工改文件
git add <文件>
git rebase --continue         # 继续下一个提交

# 如果这个提交不想要了
git rebase --skip

# 彻底放弃,回到 rebase 之前
git rebase --abort            # ← 救命命令,随时可用

实测关键输出

复制代码
$ git rebase --continue
[detached HEAD 3793fae] feat: feature 改第2行
 1 file changed, 1 insertion(+), 1 deletion(-)
Rebasing (2/2) Successfully rebased and updated refs/heads/feature.

🎯 高级技巧:git rerere(让 Git 记住你怎么解的冲突)

适用场景:长期分支反复合并、rebase 反复冲突同一个地方。

bash 复制代码
git config --global rerere.enabled true

实测效果

复制代码
--- 第 1 次 merge 冲突,我人工改成了 MERGED ---
CONFLICT (content): Merge conflict in f.txt
Recorded preimage for 'f.txt'          ← Git 记录冲突现场
Recorded resolution for 'f.txt'        ← Git 记录我的解法

--- 回滚这次 merge,再合并一次 ---
$ git merge topic
CONFLICT (content): Merge conflict in f.txt
Resolved 'f.txt' using previous resolution.    ← 自动复用了上次的解法!
$ cat f.txt
a
b MERGED      ← 直接就是我上次解出来的结果,不用动手
c

注意:rerere 只自动应用解法 ,仍需你 git add + git commit 确认。


四、后悔药:搞砸了怎么撤

场景 1:合并中,还没 commit,想重来

bash 复制代码
git merge --abort        # merge 冲突中
git rebase --abort       # rebase 冲突中
git cherry-pick --abort  # cherry-pick 冲突中

场景 2:merge 已经 commit 了,但解错了(还没 push)

bash 复制代码
git reset --hard ORIG_HEAD     # ← 最优雅,ORIG_HEAD 永远指向"危险操作前"的位置

实测

复制代码
merge 前 HEAD(ORIG_HEAD) = 4eff645   当前 HEAD = 4eff645
$ git reset --hard ORIG_HEAD
回滚后 f.txt 内容:MASTER          ← 完美回到合并前

ORIG_HEAD 是 Git 自动维护的"上一次危险操作前的位置",merge / rebase / reset / pull 都会更新它。

场景 3:merge 已 commit 且已经 push,不能 reset

bash 复制代码
git revert -m 1 <merge提交号>

实测

复制代码
错误 merge 已提交:
*   9d459ed merge: 解错了
|\
| * e05e701 f
* | 4eff645 m

$ git revert -m 1 HEAD
revert 后 f.txt = MASTER           ← 内容被反向撤销了

历史记录:
* b74ef7b Revert "merge: 解错了"    ← 新增一个反向提交
*   9d459ed merge: 解错了           ← 原记录保留(不破坏历史,别人不会受影响)
|\

-m 1 的意思是"保留第 1 个父分支(即主干)的内容"。

已 push 的公共分支一定用 revert,不要用 reset(reset 会改写历史,坑全组人)。

场景 4:什么办法都没了,找 reflog

bash 复制代码
git reflog                       # 看 HEAD 的所有移动记录
git reset --hard HEAD@{5}        # 回到第 5 步之前

reflog 是 Git 的"后悔药中的后悔药",90 天内的操作基本都能找回(包括被 reset 掉的 commit)。


五、面试 30 秒背诵版

Q:Git 冲突是怎么产生的?怎么解决?

【原因】

Git 合并用的是三路合并,比较的是"共同祖先、你的版本、对方版本"三个版本。

只有当两边都改了同一块代码 时 Git 无法判断该听谁的,才会冲突。

常见冲突类型有:同一行内容冲突、一方修改一方删除、二进制文件冲突、重命名冲突,

以及换行符 CRLF/LF、IDE 自动格式化这类"假冲突"。

【解决】

git status 看哪些文件冲突,打开冲突文件会看到

<<<<<<< HEAD=======>>>>>>> 分支名 三组标记,

上半部分是我的、下半部分是对方的。

手工改成最终想要的内容、删掉所有标记,然后 git add 标记为已解决,最后 git commit 完成合并。

如果冲突中想放弃,用 git merge --abortgit rebase --abort 一键回退。

【加分点】

  • rebase 冲突时 --ours 指的是目标分支而不是我自己的分支,用错会丢代码
  • 已 push 的错误合并用 git revert -m 1 撤销,不能用 reset
  • rerere.enabled 可以让 Git 记住解法,重复冲突自动解决
  • 冲突解决完要 grep 检查有没有残留的 <<<<<<< 标记

六、预防冲突的 8 条实战规范

# 规范 为什么
1 开工前先 git pull,每天至少一次 减少与你本地的分叉程度
2 一个功能一个分支,功能做完立刻合并删除 分支活越久,冲突越多
3 小步提交,别攒 3 天的改动一次提 提交越小冲突越好解
4 不要改别人的代码格式化 格式冲突是最没价值的冲突
5 团队统一 .editorconfig + .gitattributes 根除换行符和缩进假冲突
6 公共文件(pom.xml、配置文件、常量类)改动先在群里说一声 这类文件全组都在改,是冲突重灾区
7 长期分支定期 git rebase master 保持同步 把大冲突拆成小冲突
8 合并前 git fetch + git log HEAD..origin/master 看一眼别人改了啥 提前心里有数

推荐工作流(实习生版)

bash 复制代码
# 每天早上
git checkout master
git pull
git checkout -b feature/订单导出       # 开新分支

# 开发中,频繁小提交
git add . && git commit -m "feat(order): 新增导出接口"

# 提交 MR 前,先同步主干(把冲突在本地解决干净)
git fetch origin
git rebase origin/master              # ← 冲突在这里解决,别等 MR 页面上解
git push -f origin feature/订单导出    # rebase 后需要强推自己的分支

# MR 被 approve 后合并,删分支
git branch -d feature/订单导出

⚠️ git push -f 只能强推你自己的 feature 分支

永远不要 git push -f origin master------这是能让你当天离职的操作。


七、速查命令表

你想要什么 命令
看哪些文件冲突 git status
看冲突的 3 个版本 git ls-files -u(1=base 2=ours 3=theirs)
保留我的版本 git checkout --ours <file>
保留对方版本 git checkout --theirs <file>
放弃 merge git merge --abort
放弃 rebase git rebase --abort
rebase 继续 git add . && git rebase --continue
rebase 跳过这个提交 git rebase --skip
回到合并前 git reset --hard ORIG_HEAD
撤销已 push 的 merge git revert -m 1 <commit>
找丢失的提交 git reflog
检查残留冲突标记 grep -rn "<<<<<<<" .
看分支图 git log --oneline --graph --all
开启冲突记忆 git config --global rerere.enabled true
暂存手头改动 git stash / git stash pop

配套文件git-conflict-lab/demo.sh ------ 5 个冲突场景的完整复现脚本,bash demo.sh 即可重跑。

相关推荐
RAOY的AI笔记1 小时前
GPT-6 Astra开发指南:API调用、工具使用与AI Agent应用思路
大数据·人工智能·gpt
科技每日热闻2 小时前
中国企业出海开展业务,如何挑选可安全合规使用国际大模型的云平台?Amazon Bedrock 在同一平台完成国际模型接入、区域选择与合规治理
大数据·人工智能·安全·ai
SelectDB2 小时前
Apache Doris+ Paimon 2.0:构建 Agentic AI 数据闭环
大数据·数据库·数据分析
进击的横打2 小时前
【人工智能】AI时代公司组织架构的重构
大数据·人工智能·重构
YangYang9YangYan2 小时前
校招视角|财务数字化岗位 SQL、工具、项目完整备考指南
大数据·数据库·sql
光锥智能2 小时前
小鹏第一位机器人自己走下产线
大数据·人工智能
GISMagic3 小时前
Agent学习,写在开始之前
大数据·学习·agent
Capricorn19883 小时前
知芽 Notebook Skill 引用存在性校验与单元记忆破解幽灵引用危机
大数据·论文阅读·人工智能·论文笔记
hgfsjk3 小时前
跨境电商ERP系统软件哪个好?2026年选型指南
大数据·经验分享·笔记·其他