14. 合并冲突不是噩梦:Git 合并策略与冲突解决
「Git」系列已发布: 00. 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 | 01. Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 | 02. Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 | 03. add/commit/push 别再死记:Git 工作流核心概念实操 | 04. Git 分支管理:5 步彻底搞懂 branch 与 merge | 05. clone/fetch/pull/push 分不清?Git 远程协作实战 | 06. 给版本打上永久印记:Git 标签管理与发布实战 | 07. 切分支怕丢改动?Git stash 暂存恢复保护现场 | 08. rebase 不是 merge 的替代品:Git 故事线重构进阶 | 09. Git 日志与调试:log/blame/diff 让历史无所遁形 | 10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工 | 11. 多仓库依赖怎么管?Git 子模块统一版本一键更新 | 12. GitFlow vs GitHub Flow vs 主干:团队工作流 3 选 1 | 13. 仓库越来越大?Git LFS 让图片视频不再撑爆仓库 | 14. 合并冲突不是噩梦:Git 合并策略与冲突解决(本文) | 15. 常用命令记不住?Git 别名让操作变成快捷键
merge、rebase、squash 该怎么选?冲突来了怎么解?本文拆解合并策略与冲突解决套路,让多人协作如丝般顺滑。
分支管理(第 04 篇)和变基操作(第 08 篇)要很熟。建议拿个测试仓库刻意制造冲突来练手——冲突这东西,多碰几次就不怕了。
一、合并策略
1.1 快进合并(Fast-Forward)
目标分支没新提交时,Git 直接移指针,不生成合并提交:
快进前,main 落后 feature:
gitGraph commit id: "A" commit id: "B" branch feature commit id: "C" commit id: "D"
git merge feature 后 main 直接快进到 D,形成线性历史(无合并提交):
gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D"
git checkout main
git merge feature
禁用快进
——强制生成合并提交:
git merge --no-ff feature
强制生成合并提交 M,保留 feature 分支的完整历史:
gitGraph commit id: "A" commit id: "B" branch feature commit id: "C" commit id: "D" checkout main commit id: "M" merge feature
1.1.1 三方合并
两个分支都有新提交时,Git 用三方合并算法:
三方合并涉及三个点:共同祖先(base)、当前分支(ours)、合入分支(theirs),Git 对两边差异做合并:
gitGraph commit id: "A" commit id: "B" commit id: "E" commit id: "F" branch feature commit id: "C" commit id: "D" checkout main merge feature
git checkout main
git merge feature
Git 做三件事:① 找共同祖先 B;② 算 B→F 的差异(ours);③ 算 B→D 的差异(theirs);④ 合并差异,生成合并提交。
1.1.2 压缩合并(Squash)
把源分支所有提交压成一个,应用到目标分支:
git merge --squash feature
git commit -m "feat: 实现用户认证模块"
feature 分支的历史不会出现在 main 上,但 feature 分支本身还能继续开发。
1.2 冲突怎么来的
Git 自动合并失败就产生冲突。最常见几种情况:
- 两个分支改了同一个文件的同一行。
- 一个分支删了文件,另一个分支改了同一个文件。
- 两个分支都新增了同名文件但内容不同。
# 遇到冲突
git merge feature
# Auto-merging src/config.py
# CONFLICT (content): Merge conflict in src/config.py
# Automatic merge failed; fix conflicts and then commit the result.
# 看哪些文件冲突
git status
# Unmerged paths:
# both modified: src/config.py
1.2.1 冲突标记
Git 在冲突文件里插标记:
<<<<<<< HEAD (ours / 当前分支)
db_host = "localhost"
db_port = 3306
=======
db_host = os.getenv("DB_HOST", "prod-db.example.com")
db_port = int(os.getenv("DB_PORT", "5432")
>>>>>>> feature (theirs / 合进来的分支)
<<<<<<<到=======是你当前的。=======到>>>>>>>是要合进来的。
1.3 怎么解决
1.3.1 手动解决
# 1. 编辑冲突文件,删标记,留正确代码
vim src/config.py
# 最终效果:
db_host = os.getenv("DB_HOST", "localhost")
db_port = int(os.getenv("DB_PORT", "5432")
# 2. 标记已解决
git add src/config.py
# 3. 完成合并
git commit
# 编辑器弹出,合并提交信息已自动填好
1.3.2 选一方
# 全要当前分支的
git checkout --ours src/config.py
git add src/config.py
# 全要合进来的
git checkout --theirs src/config.py
git add src/config.py
# 注意:rebase 冲突时反过来!
# rebase: ours = 目标分支, theirs = 当前分支
1.3.3 用合并工具
# VS Code
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# 启动
git mergetool
# 其他
git config --global merge.tool meld
git config --global merge.tool kdiff3
git config --global merge.tool opendiff # macOS
1.4 合并策略选项
1.4.1 ours vs theirs
# 忽略合进来的分支,完全以当前为准
git merge -s ours feature
# rebase 中用 theirs(冲突时自动选 feature 版本)
git rebase -Xtheirs main
1.4.2 合并算法
某些结构复杂的文件,默认算法可能产生糟糕的冲突标记:
# patience:对重复内容多的文件更好
git merge -Xpatience feature
# histogram:patience 的改进版,更快
git merge -Xhistogram feature
1.4.3 忽略空白
# 忽略所有空白差异
git merge -Xignore-all-space feature
# 只忽略行尾空白
git merge -Xignore-space-at-eol feature
1.5 高级冲突场景
1.5.1 重命名冲突
当前分支把file.py改名为module.py,合并分支修改了file.py。Git 通常能自动处理,检测不到的话:
# 降低重命名检测阈值——30% 相似就视为重命名
git merge -Xfind-renames=30 feature
如果还是出了两个文件:
git status
# added by us: module.py
# added by them: file.py
# 手动把合并分支的修改合到重命名后的文件
cat file.py >> module.py
git rm file.py
git add module.py
1.5.2 二进制文件
二进制没法像文本那样手动合并,通常只能选一方:
# 选当前分支的
git checkout --ours logo.png
git add logo.png
# 选合并分支的
git checkout --theirs logo.png
git add logo.png
# 建议:在 .gitattributes 里给二进制配合并驱动
# *.png merge=binary-ours
1.5.3 子模块冲突
# 看子模块差异
git diff main feature -- path/to/submodule
# 切到其中一方的版本
git checkout main -- path/to/submodule # 或
git checkout feature -- path/to/submodule
git add path/to/submodule
1.6 冲突怎么防
1.6.1 频繁变基
# feature 分支上定期 rebase 到 main
git checkout feature
git rebase main
# 每次冲突都很少,总比攒到最后一次性搞强
1.6.2 小步提交、短命分支
大型 feature 分支开发几周后一次性合并——这是典型的反模式。每天把 main 合并进来,或者把分支拆小。
1.7 Code Review 预判
Merge Request 里如果和正在进行的其他 MR 动了同一文件,提前协调修改顺序。MR 描述里标一下"可能和 MR XX 冲突"。
1.8 解决后检查清单
[ ] 冲突标记(<<<<<<<, =======, >>>>>>>)全清干净了吗?
[ ] 代码编译能过吗?
[ ] 相关测试跑通过了吗?
[ ] 合并后逻辑完整吗(没漏掉两边的功能)?
[ ] 有没有重复的代码块?
[ ] 有没有语法错误(比如重复的闭合括号)?
[ ] .gitignore 有没有误伤需要的文件?
1.9 文章引用
- Pro Git - 合并:https://git-scm.com/book/zh/v2/Git-分支-分支的新建与合并
- Git 合并策略文档:https://git-scm.com/docs/merge-strategies
- Atlassian 冲突解决教程:https://www.atlassian.com/git/tutorials/using-branches/merge-conflicts
- Git mergetool 文档:https://git-scm.com/docs/git-mergetool
📌 本文首发于微信公众号 【运维知识点滴】——专注运维实战干货,微信搜索“运维知识点滴”或扫下方二维码关注,更新不迷路。
| 微信公众号【运维知识点滴】 | 微信小程序【A5单据合并&大试卷拆分转A4打印】 |
|---|---|
![]() |
![]() |
| 运维实战干货 · 关注追更 | Word 转 PDF · 试卷/发票打印神器 · 免费 |
💡 小程序【A5单据合并&大试卷拆分转A4打印】是干嘛的? 一个 Word 转 PDF 的打印神器,专治“文档版式和打印机对不上”:
- A3 试卷打不出来? 学校发的电子版试卷多是 A3,家用打印机只吃 A4——上传 Word 一键转换:可缩到一页 A4(页数不变),也可对半拆成两张 A4(1:1 原大、字更清晰),两种模式自选;
- 版式不乱:真实渲染引擎原样转换、绝不重排内容,转出的 PDF 和原 Word 排版一致,公式/表格/图片不跑版;
- 两张 A5 单据拼一张 A4:出租车票、火车票等报销凭证拼版后,一次打印、整齐粘贴;
- 隐私省心:无需注册,转换结果留存 5 天、最多下载 5 次即自动删除,不留任何历史记录;
- 微信扫码即用,免费、免安装,支持 doc / docx(≤50MB)。
上一篇:13. 仓库越来越大?Git LFS 让图片视频不再撑爆仓库 | 下一篇:15. 常用命令记不住?Git 别名让操作变成快捷键

