Git学习(3)合并冲突全解析:从原理到实战解决

🔥keyipatience:个人主页

🎬作者简介:C/C++后端开发学习者

🌟专栏传送门:《c++》《linux》《c++高阶数据结构》《c++数据结构与算法》《前端学习》

⭐️patience is key in life

Git 合并冲突

先看懂冲突是怎么制造出来的

核心前提:两个分支,在同一个文件的同一行,都做了修改并且各自提交了,Git 不知道该保留哪一个,就产生冲突。

整套复现流程

复制代码
# 1. 创建并且直接切换到 dev1 分支(一步到位)
git checkout -b dev1
# 等价于 git branch dev1 && git checkout dev1

在 dev1 修改 ReadMe: 原来行 write aaa for new branch → 改成 write bbb for new branch

复制代码
git add .
git commit -m "modify ReadMe"

# 2. 切回 master
git checkout master

这里 master 里还是老的 write aaa for new branch,然后在 master 上,同样这一行 ,改成 write ccc for new branch,再提交:

复制代码
git add .
git commit -m "modify ReadMe"

现在历史分叉了:

  • 共同祖先提交:内容是 aaa
  • dev1:改成 bbb,提交一次
  • master:改成 ccc,提交一次 两条分支各自往前走,同一个位置改了不一样的内容,无法自动快进合并

执行合并,触发冲突

现在在 master,执行合并:

复制代码
git merge dev1

输出关键提示:

复制代码
Auto-merging ReadMe
CONFLICT (content): Merge conflict in ReadMe
Automatic merge failed; fix conflicts and then commit the result.

含义: Git 尝试自动合并,但是内容冲突,合并暂停了,进入「未合并状态」

查看状态:

复制代码
git status

会看到:both modified: ReadMe 意思:这个文件,两边分支都修改了

两个选择写在提示里:

  1. 改完冲突,git add + git commit 完成合并
  2. git merge --abort 放弃这次合并,回到合并前干净状态

冲突标记怎么读

打开 ReadMe,Git 自动插入标记:

复制代码
<<<<<<< HEAD
write ccc for new branch
=======
write bbb for new branch
>>>>>>> dev1
  • <<<<<<< HEAD <<<<<<< 是标记开头; HEAD 就代表:你现在正在待着的分支 (本次是 master)。 这一段里面的文字,是 master 分支上写的内容:write ccc for new branch。

  • ======= 分割线,把两边分支的改动隔开,上面是当前分支,下面是要合并过来的分支。

  • >>>>>>> dev1 >>>>>>> 是标记结尾; dev1 是另外那个分支的名字。 这一段里面的文字,是 dev1 分支提交的内容:write bbb for new branch。

这里 Git 不会替你决定保留 ccc 还是 bbb,必须人来手动决定 你可以:

  • 保留 master 的 ccc
  • 保留 dev1 的 bbb
  • 自己写成新内容(比如 write bbb and ccc) 同时必须删掉 <<<<<<<=======>>>>>>> 这三行标记符号!

示例:我们选择保留 bbb,修改后文件:

复制代码
hello bit
hello git
hello world
hello version1
hello version2
hello version3
write bbb for new branch

解决冲突之后,必须手动提交!(和快进合并最大区别)

改好文件,删除冲突标记:

复制代码
git add .
git commit -m "merge ReadMe"

这次 commit 是专门的合并提交(merge commit) 原理图变化: master 指针往前走一步,生成一个新的提交节点,同时指向 master 原来的提交、dev1 的提交,两条分叉的线在这里交汇。

对比:

  • Fast-forward 快进合并:没有新提交,不用手动 commit
  • 冲突合并 / 分叉无冲突合并:产生 merge 合并提交,需要 commit

合并完成,dev1 的提交已经被并入 master,可以安全删除:

复制代码
git branch -d dev1

Git 分支管理(Fast-forward /--no-ff)

快进合并(Fast-forward)的本质:master 没有新提交时,合并 dev 只是把 master 指针直接移动到 dev 的提交上,不会生成新的合并提交

核心问题:

  • 合并完成、删除 dev 分支后,提交历史是一条完整直线
  • 从 git log 中看不出这段提交来自分支合并,无法区分是直接在 master 提交,还是从其他分支合入
  • 分支开发的轨迹被抹除,不利于团队追溯问题、回滚版本

对比之前冲突场景的非快进合并: 两条分支各自有新提交,Git 自动生成双父合并提交 。哪怕删除 dev1 分支,git log --graph 依然能看到分叉、汇合的分支轨迹,完整保留合并信息。

--no-ff:强制禁用快进合并

--no-ff = no fast-forward,强制关闭快进模式。 哪怕当前合并完全满足快进条件,Git 也会强制生成一个新的合并提交,保证分支历史保留清晰的分叉、汇合痕迹。

命令格式

复制代码
git merge --no-ff -m "合并说明文字" 分支名

输出提示:Merge made by the 'recursive' strategy.,代表不是快进合并

禁用快进后一定会生成新提交,因此可以用 -m 直接指定本次合并提交的备注。

执行 git log --graph --pretty=oneline --abbrev-commit,可以看到清晰的分叉图形:

复制代码
*   5bd16b4 (HEAD -> master) merge with no-ff
|\
| * 41b882f (dev2) modify ReadMe
|/

即使后续删除 dev2 分支,这段分叉合并的历史也会永久保留。

通用分支管理策略

1.master 主分支

  • 性质:绝对稳定
  • 用途:仅用于发布正式版本,日常不直接在上面开发
  • 所有合入 master 的代码,必须经过测试、可直接上线

2. dev 开发分支

  • 性质:开发集成分支,持续迭代
  • 用途:日常开发的主分支,所有功能开发最终都汇合到这里
  • 版本开发完成、测试通过后,再从 dev 合并到 master 发布

3. 个人 / 功能分支(michael、bob 等)

  • 团队成员开发新功能、修复 bug 时,都从 dev 拉出独立分支
  • 开发完成、自测通过后,合并回 dev 分支
  • 多人并行开发互不干扰,降低代码冲突概率

最终形成多层结构:master 最稳定,dev 是开发主线,下方挂载大量短期功能 / 个人分支,实现并行开发与版本稳定的平衡。

Git bug 分支 + git stash

想一想你正在 dev2 分支写代码,写到一半,代码还没提交(工作区有未保存的修改),这时 master 线上爆出紧急 bug,需要立刻修复。

难点:Git 切换分支时,工作区未提交的改动会跟着走,会污染要修复 bug 的 master 分支 ,不能直接切分支! 解决方案:git stash 储藏(暂存)当前正在写的代码现场。

stash 相关全套命令

git stash 储藏当前工作区 + 暂存区的改动(清空工作区、清空暂存区 ,回到干净的状态(回到上一次 commit 的样子),储藏之后 git status 查看,工作区变成干净状态,可以安心切换别的分支。

储藏的内容是临时的快照,不在正式 commit 历史里。

git stash list 查看所有储藏记录,格式 stash@{0}、stash@{1},数字越大代表越早储藏。

git stash pop 恢复最新的储藏(stash@{0}),恢复之后自动删掉这条 stash 记录

git stash apply 恢复储藏,但是不会删掉 stash 记录,储藏还在列表里

恢复指定储藏:git stash apply stash@{编号} 适合多次 stash、需要选择性恢复的场景。

完整流程

阶段 1:储藏当前dev2 的开发现场

复制代码
git stash          # 保存dev2写到一半的代码,工作区变干净
git status         # 验证:working tree clean

如果没有stash直接切master:

阶段 2:切 master,创建临时 bug 分支 fix_bug,去修复线上 bug

复制代码
git checkout master
git checkout -b fix_bug   # 新建bug修复分支,基于master
# 修改文件,修复bug
git add ReadMe
git commit -m "fix bug"

阶段 3:bug 修好,合并回 master,删除临时 bug 分支

复制代码
git checkout master
git merge --no-ff -m "merge fix_bug branch" fix_bug
git branch -d fix_bug   # 删除用完的bug临时分支

用了 --no-ff,保留这次 bug 修复的合并记录

阶段 4:切回 dev2,把之前储藏的开发现场恢复回来,继续开发

复制代码
git checkout dev2
git stash list        # 看到之前存的 stash@{0}
git stash pop         # 恢复代码,同时删除这条stash
# 继续写代码,写完正常提交
git add .
git commit -m "modify ReadMe"

这里一个非常关键的坑 : **fix_bug 修复的 bug 代码,只合并到 master,dev2 分支里是看不到这个 bug 修复代码的!**dev2 当初是从老版本 master 拉出来的;master 现在已经往前走了一个 bug 修复提交,dev2 的提交历史还停留在旧节点,两者分叉了。

怎么让 dev2 拿到 master 里已经修好的 bug 代码?

错误做法:直接切 master,合并 dev2。

风险:合并冲突会直接在 master(稳定上线分支) 上解决。 如果冲突很多、改错了,会直接把线上 master 主分支搞坏,非常危险!

复制代码
git add .
git commit -m "merge master"

规范做法:

**先在自己的开发分支(dev2)里面合并 master,冲突在 dev2 本地解决、测试没问题;最后再切回 master 合并 dev2。**不会污染稳定的 master 分支。

当前在 dev2,执行合并 master

复制代码
git merge master

很大概率出现冲突

打开文件,手动处理冲突标记 <<<<<<< HEAD / ======= / >>>>>>> master,改完保存

解决冲突之后,add + commit

相关推荐
Paper数据分析-经管学术1 小时前
2000-2025年国家级大数据综合试验区政策试点DID面板数据
大数据
Angeldream1231 小时前
第 9 章 医学影像项目实战
大数据·图像处理·人工智能·python·医学影像处理
wasee0011 小时前
三四线城市适合投资XR大空间吗?投入、客流与回本周期测算
大数据·人工智能
Raas1001 小时前
MAI Gateway选购攻略:开源AI网关有哪些?避坑指南与对比
大数据·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
段一凡-华北理工大学1 小时前
高炉能源系统智能化:热风炉、煤气与极致能效~系列文章03:热风炉烧炉与风温的决定因素
大数据·linux·人工智能·能源·热风炉·高炉能源优化
updayday8541 小时前
电脑换了硬盘、加了内存,资产台账却没更新 - Ping64 硬件资产管理
大数据·网络·数据库·安全·智能路由器
jinggongszh1 小时前
参数退潮,Know-how上台:工业AI的竞争为什么换了赛场?
大数据·人工智能·数据闭环·工业ai
刘老高1 小时前
微服务架构实战:ShardingSphere Proxy + Canal 分库分表与 ES 同步全链路
elasticsearch·微服务·架构
Elasticsearch1 小时前
Elastic 重大事件功能如何将 100 条日志样本转化为检测规则
elasticsearch