Git 合并冲突完全指南:分支合并、拉取冲突、推送冲突

前言

Git 的冲突总是让新手头疼,尤其是搞不清"合并冲突""拉取冲突""推送被拒"到底有什么关系。本文将从分支合并冲突 出发,再分别拆解 git pull 拉取冲突git push 推送被拒 的成因与解决方案,最后点明它们的内在联系,让你一次彻底看懂所有冲突场景。

一、分支合并冲突(git merge 冲突)

1.1 什么是合并冲突

两个分支修改了同一个文件的同一处代码 ,Git 无法自动判断该保留哪一份修改,就会产生合并冲突,必须人工介入。

举例:

  • master 分支:test.py 第1行是 print("你好")
  • master 新建 dev 分支,把同一行改成 print("Hello")
  • dev 合并回 master,Git 分不清该保留中文还是英文,冲突产生。

1.2 必然冲突的场景

  • 同一文件 同一行 两边都做了修改
  • 一边删除文件,另一边修改了该文件
  • 两边对同一处代码分别做了删除和新增(逻辑对立)

1.3 不会冲突的场景

  • 修改的是 不同文件
  • 同一文件修改 不同行(Git 可自动拼接)
  • 只新增文件,不修改已有代码

1.4 冲突标记长什么样

冲突文件内会出现这样的标记:

复制代码
<<<<<<< HEAD         # 当前所在分支的代码
print("你好")
=======              # 分割线
print("Hello")
>>>>>>> dev          # 被合并分支的代码

处理规则: 删除所有 <<<<<<<=======>>>>>>> 标记,保留最终想要的代码,保存文件即可。

1.5 解决冲突的标准流程

复制代码
# 1. 查看冲突文件
git status

# 2. 手动编辑冲突文件,删除标记,保留最终代码

# 3. 标记冲突已解决
git add 冲突文件名
# 或一次性添加所有冲突文件
git add .

# 4. 完成合并提交
git commit

1.6 撤销合并冲突

如果解决过程中改乱了,或者不想合并了,一键回退:

复制代码
git merge --abort

这条命令会撤销本次合并,回到执行 merge 前的状态。对所有由 merge 引起的冲突(包括 pull)均有效。

二、git pull 拉取冲突

2.1 git pull 的本质

git pull = git fetch(下载远程代码) + git merge(合并到本地)

因此,拉取冲突的本质就是一次合并冲突 ,只不过合并的对象不是本地分支,而是远程跟踪分支(如 origin/master)。

2.1.1 深入拆解:git pull 的两个阶段与修复逻辑

fetch 阶段(已 100% 完成)

  • 远程仓库所有最新提交、文件、修改全部下载到本地隐藏的远程追踪分支 origin/master,不存在漏下载。
  • 此时本地已经完整拿到远程所有代码,只是还没和当前本地分支融合。

merge 阶段(可能中途卡住)

  • Git 尝试自动把下载来的远程代码合并进本地分支。
  • 如果同一行代码存在冲突无法自动判断,Git 会直接暂停合并流程。此时状态为:
    • 本地原有代码还在
    • 远程代码已经存在本地缓存里
    • 两个版本没有融合成一份完整代码,合并动作停在半路

为什么修复冲突后不用再 pull?

因为 pull 的核心作用是 fetch(下载远程数据),而下载这一步早就做完了。后续手动改冲突文件、git addgit commit,本质是在手动完成剩下的 merge 合并步骤:

  • 编辑冲突文件 = 人工告诉 Git 最终保留什么代码
  • git add = 标记该文件冲突已修复
  • git commit = 生成一条合并提交,正式把远程代码和本地代码融合为完整分支

提交完成后,本地分支就同时包含本地修改 + 远程全部更新,远程没有任何新代码需要再次下载,自然不需要重复 pull

2.2 拉取时无冲突的 3 种情况

  1. 远程新增全新文件 (本地没有)
    例如仓库初始化时自动生成的 README.md,本地不存在,pull 直接下载,无冲突。
  2. 修改同一文件的不同行
    本地改第1行,远程改第10行,Git 自动将两处修改合并。
  3. 只有一端修改文件,另一端未改动
    直接覆盖更新,无需人工干预。

2.3 拉取时触发冲突的唯一条件

本地和远程修改了同一个文件的同一行代码(或文件删除与修改同时发生)。

此时终端会出现类似提示:

复制代码
Automatic merge failed; fix conflicts and then commit the result.

冲突标记与分支合并完全一致,只是 >>>>>>> 后面显示的是远程分支名,例如 >>>>>>> origin/master

2.4 拉取冲突的完整解决步骤

复制代码
# 1. 拉取触发冲突
git pull origin master

# 2. 查看冲突文件
git status

# 3. 手动编辑冲突文件,删除 <<<<< , ===== , >>>>> 标记,保留最终代码

# 4. 标记冲突已解决
git add 冲突文件
# 或
git add .

# 5. 完成合并提交
git commit

重要结论: 执行完 git commit 后,本地仓库已包含本地修改 + 远程最新代码,此时不需要再次 git pull,直接推送即可。

复制代码
# 6. 直接推送
git push origin master

三、git push 推送被拒(push rejected)

3.1 推送被拒的本质

当你在推送时遇到报错:

复制代码
! [rejected] master -> master (fetch first)

原因: 远程仓库存在你本地没有的提交记录,你的本地分支进度落后于远程,Git 禁止你直接覆盖远程分支。

推送被拒本身不是冲突,而是一个"你必须先同步"的提醒。 真正的冲突依然发生在你执行 pull 合并的时候。

3.2 两种细分情况

情况一:无代码冲突,仅进度落后
  • 远程新增了 README.md 或别人推送了新功能文件,你本地完全没有碰过这些文件
  • 远程的提交与你的本地修改没有任何逻辑重叠

解决流程:

复制代码
# 1. 先拉取远程更新
git pull origin master
# 此时会自动合并,无冲突弹出

# 2. 直接推送
git push origin master

特殊情况: 如果你本地仓库和远程仓库没有任何共同提交历史 (例如本地新建的仓库,远程却有初始化的 README.md),直接 git pull 会报错:

fatal: refusing to merge unrelated histories

此时需要加上 --allow-unrelated-histories 参数来允许合并:

复制代码
git pull origin master --allow-unrelated-histories

此操作通常无冲突,自动合并成功后,再 git push 即可。

情况二:拉取后出现代码冲突(推送失败,一定要先拉取再推送合并冲突的文件)
  • 远程和本地修改了同一个文件的同一处代码
  • pull 后触发合并冲突,必须手动解决

解决流程(完整版):

复制代码
# 1. 提交本地修改(如果尚未提交)
git add .
git commit -m "本地修改"

# 2. 拉取远程代码,触发冲突
git pull origin master

# 3. 手动编辑冲突文件,删除冲突标记,保留最终代码

# 4. 标记冲突已解决
git add .

# 5. 完成合并提交
git commit

# 6. 推送到远程
git push origin master

3.3 安全操作铁律

永远不要用 -f 强制推送 来覆盖别人的提交,除非你百分百确定这是个人仓库且没有协作。

标准流程只有一句话:先 pullpush

即使你刚刚解决完拉取冲突,提交后本地已领先远程,直接 push 即可,无需再次拉取。

四、拉取冲突 vs 推送被拒:区别与联系

对比维度 拉取冲突 (pull conflict) 推送被拒 (push rejected)
发生时机 执行 git pull 执行 git push
直接原因 本地与远程修改了同一文件的同一位置 远程有本地不存在的提交记录
本质 这是一次合并冲突 这是一个推送拦截,不是冲突本身
解决方式 手动编辑冲突文件,提交合并 git pull 同步,若有冲突则按左列处理
最终处理 都是在解决合并冲突 都需要通过 pull 合并代码后才能推送
危险操作 随意删除冲突标记可能丢代码 使用 push -f 会覆盖远程仓库,协作时严禁

一句话总结:所有冲突的本质都是合并冲突。

push 被拒只是 Git 提醒你"需要先合并",而合并过程中可能产生冲突,那才是真正需要你手动解决的地方。

五、实战案例串讲

案例 1:分支合并冲突(本地 dev → master)

  1. git checkout master
  2. git merge dev
  3. 提示冲突,文件出现 <<<<<<< HEAD ... >>>>>>> dev 标记
  4. 手动编辑文件,删除标记,保留最终代码
  5. git add .git commit
  6. 冲突解决完毕,可以继续开发或推送

案例 2:拉取冲突(远程已更新,本地也有修改)

  1. 本地修改了 models.py 第5行,提交本地
  2. 同时同事也修改了远程 models.py 第5行,并推送
  3. 你执行 git pull origin master,触发冲突
  4. 按标记解决,git addgit commit
  5. 此时本地已包含双方修改,直接 git push 成功

案例 3:推送被拒 → 拉取无冲突(远程新增文件,且无关联历史)

  1. 你本地开发完毕,执行 git push 被拒:fetch first
  2. 执行 git pull origin master,却被提示 fatal: refusing to merge unrelated histories
  3. 因为你本地仓库和远程仓库没有共同历史(例如你本地是 git init 的新仓库,远程是建仓库时自动生成了 README.md
  4. 执行 git pull origin master --allow-unrelated-histories,自动合并无冲突
  5. 合并提交生成后,直接 git push 成功

案例 4:推送被拒 → 拉取产生冲突(远程修改了同一文件)

  1. git push 被拒
  2. git pull origin master 触发冲突,文件出现 <<<<<<< HEAD ... >>>>>>> origin/master
  3. 手动解决冲突,git add .git commit
  4. git push origin master 成功

六、避坑技巧汇总

  1. 养成先 pull 后 push 的习惯,避免被拒和冲突堆积
  2. 小粒度频繁提交,每次改动少,冲突概率降低,解决也容易
  3. 团队分工明确,避免多人同时修改同一个核心文件
  4. 冲突解决后务必编译/测试,确保合并后的代码逻辑正确
  5. 善用图形化工具(VSCode、GitKraken、Sourcetree),直观对比双方修改,大幅降低出错率
  6. 不确定时用 git merge --abort 撤销,安全回到合并前状态,重来一遍不丢代码
  7. 跨仓库首次合并时,留意 unrelated histories 提示 ,合理使用 --allow-unrelated-histories 参数

总结

  • 分支合并冲突:本地两分支修改了同一行代码,需要手动解决。
  • 拉取冲突git pull 时本地与远程修改了同一行,本质上仍是合并冲突。
  • 推送被拒 :远程有本地不知道的提交,Git 禁止推送,你必须先 pull(可能会触发冲突),再 push
  • 核心原则 :所有冲突都是合并冲突,用同一套流程解决;push 被拒只是合并前的预警,按规范先拉后推即可安全化解。
  • 特殊情形 :若 pull 时提示拒绝合并不相关历史,加上 --allow-unrelated-histories 即可继续。
相关推荐
NutShell Wang2 小时前
拆解 GitHub gh-stack:堆叠 PR 工作流的设计取舍与工程实现
前端·git·开源·github·代码复审·开发者工具·vibe coding
.Hypocritical.15 小时前
Git 入门教程
git
AI行业学习21 小时前
Claude Code + cc-switch + Git + Node.js 全套下载+安装+配置完整版
开发语言·git·python·前端框架·node.js·html·notepad++
豆角焖肉1 天前
.gitignore:配置文件+初学入门
git·gitignore
月落归舟1 天前
Git 新手入门指南
git
达达车1 天前
git使用技巧记录
git·使用技巧·版本管理
Simon—欧阳1 天前
Git使用心得&理解
git
AI行业学习1 天前
Claude Code + cc-switch + Git + Node.js 一站式完整安装配置教程【8.3】
git·python·安全·前端框架·node.js·html·notepad++
AI行业学习1 天前
Claude Code + cc-switch + Git + Node.js 一站式完整安装配置教程(2026最新·国内可用版)
人工智能·git·python·安全·node.js·html·notepad++