当前位置: 代码网 > it编程>编程语言>其他编程 > Git代码冲突原因与解决方法全解

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

2026年09月09日 其他编程 我要评论
本文所有报错信息、冲突文件内容、命令输出均为 git 2.43 真实运行截图,不是凭记忆写的。配套可复现脚本:git-conflict-lab/demo.sh(bash demo.sh 一键重跑全部场

本文所有报错信息、冲突文件内容、命令输出均为 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,所以不会产生 <<<<<<< 标记,只能二选一。

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

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

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

$ 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

三种选择

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

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

11、换行符不一致(crlf vs lf)—— windows 用户专属噩梦

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

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

解决

# 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

12、ide 自动格式化 / 自动 import 导致大面积冲突

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

原因:idea 的 reformat codeoptimize imports 把同事的代码也重排了;
或者同事用了不同的 code style(缩进 2 空格 vs 4 空格)。

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

# .editorconfig
root = true

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

[*.java]
indent_size = 4

13、文件名大小写不一致

现象:本地好好的,推到服务器上 ci 就冲突/报错。
原因:windows 和 macos 默认不区分文件名大小写ordervo.javaordervo.java 是同一个文件;
linux 区分,是两个文件。
解决git config core.ignorecase false(linux 上),并统一命名规范。

14、文件权限变更(mode change)

diff --git a/deploy.sh b/deploy.sh
old mode 100644
new mode 100755

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

15、子模块(submodule)冲突

conflict (submodule): merge conflict in libs/common

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

git checkout --ours libs/common   # 或 --theirs
git add libs/common
git submodule update --init --recursive

16、目录 / 文件类型冲突

conflict (file/directory): there is a directory with name config in feature.
adding config as config~head

原因:你建了个 config 目录,同事建了个 config 文件。
解决:手动决定保留哪个,git rm 掉另一个。

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

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

# ① 看清楚哪些文件冲突了
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 这些标记提交上去了,代码直接编译不过。

提交前自检

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

三种解决策略

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

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

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

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

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/传入  │
│  (本地)       │   (最终结果) │  (对方分支)   │
└──────────────┴──────────────┴──────────────┘
      ↑ >> 接受左边        ↑ 中间可直接编辑      接受右边 << ↑
  1. 也可以直接在中间的 result 栏手打(推荐,最灵活)
  2. apply → 文件自动 git add
  3. 全部解决完 → git commit

idea 快捷键:冲突文件上按 alt+m(win)/ ⌥+m(mac)快速打开合并界面。

vs code:冲突文件会显示 accept current change / accept incoming change / accept both changes / compare changes 四个按钮,一键解决。

命令行党的 mergetool

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

rebase 冲突专用流程

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 反复冲突同一个地方。

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,想重来

git merge --abort        # merge 冲突中
git rebase --abort       # rebase 冲突中
git cherry-pick --abort  # cherry-pick 冲突中

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

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

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

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 看一眼别人改了啥提前心里有数

推荐工作流(实习生版)

# 每天早上
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>
放弃 mergegit merge --abort
放弃 rebasegit rebase --abort
rebase 继续git add . && git rebase --continue
rebase 跳过这个提交git rebase --skip
回到合并前git reset --hard orig_head
撤销已 push 的 mergegit 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 即可重跑。

以上就是git代码冲突原因与解决方法全解的详细内容,更多关于git代码冲突原因与解决的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com