
你和小伙伴各自在 feature 分支上改代码,最后要把两条线的改动合到一起,却分不清该用 merge 还是 rebase?Git 合并分支的核心答案是:最常见的有三种写法——快进合并(fast-forward)、三方合并(--no-ff 保留合并提交)、以及变基合并(rebase 再 fast-forward),选哪个取决于你想不想保留“合并历史”。本文基于 Git 2.4x 验证,把三种写法的命令、提交图变化和适用边界讲透,并附踩坑清单。看完你就能按团队约定选对写法。今天这篇文章,编程狮就把这块讲透。
先看结论
| 你的目标 | 该用的写法 | 一句话命令 |
|---|---|---|
| 不保留合并提交、历史线性 | 快进合并 | git merge 分支名 |
| 必须保留合并节点 | 三方合并 | git merge --no-ff 分支名 |
| 历史干净、像没分过叉 | 变基合并 | git rebase 主干分支 |
💡 小提示:很多团队约定“个人分支用 rebase、集成分支用 --no-ff merge”,目的是既保留发布节点又让历史好读。
一、Git 合并分支到底在做什么
Git 合并分支,本质是把两个分支的提交历史连到一处。Git 会先找到“共同祖先提交”,再决定用哪种策略把差异合起来。理解这一点,后面三种写法就只是“连法不同”。
⚠️ 注意:合并前先
git status确认工作区干净。如果有未提交的改动,Git 可能拒绝合并或触发意料外的冲突处理。
先看最基础的 git merge 命令:
# 切换到要并入的目标分支(通常是主干)
git checkout main
# 把 feature 分支的改动合并进来
git merge feature
上面这段做的是:在 main 分支上执行 git merge,Git 会自动挑选合并策略。如果你还不熟悉分支基础,可以先过一遍 Git 教程,把分支与提交的骨架铺平。
二、方案一:快进合并(fast-forward)
快进合并发生在“目标分支没有新提交、只是落后于待合并分支”时。Git 不做新提交,只是把 main 的指针直接快进到 feature 的末端,历史变成一条直线。
# main 落后于 feature 且中间无分叉时,默认就是快进
git merge feature
# 想强制快进(不行就报错,避免意外产生合并提交)
git merge --ff-only feature
上面这条 git merge --ff-only feature 的优点是历史绝对线性、好回溯;代价是丢失了“曾经分叉合并过”的痕迹。适合个人功能分支合回本地主干的场景。

💡 小提示:常用命令记不住时,Git 速查手册 可以把 merge / rebase 的参数摊开对照着看。
三、方案二:三方合并(--no-ff 保留合并提交)
当目标分支自己也有新提交、两边分了叉,Git 会做“三方合并”:取共同祖先、两边末端三个点,生成一个全新的“合并提交”。加 --no-ff 可以强制即使能快进也要保留这个合并节点。
# 即使能快进也生成一个合并提交,保留分叉痕迹
git merge --no-ff feature -m "合并 feature 分支"
# 查看提交图,合并提交会显示两个父节点
git log --graph --oneline --decorate -n 10
三方合并的代价是多出一个合并提交,但好处明显:发布时你能一眼看到“这一批改动从哪个分支合进来”。这也是很多公司集成分支的强制约定。
四、方案三:变基合并与横向对比
变基合并的思路不同:它先把 feature 的提交“挪到” main 末端之后,再快进,最终结果是完全线性的历史,仿佛从没分过叉。
# 在 feature 分支上,把基底接到 main 最新提交之后
git checkout feature
git rebase main
# 再回到 main 快进合入
git checkout main
git merge feature
上面 rebase 之后 git merge 必然是快进,因为基底已经对齐。注意 rebase 会改写提交哈希,所以“已推送到远程、别人可能基于它工作”的分支禁止 rebase。
把三种写法摆在一起对比:
| 方案 | 适用场景 | 优点 | 代价 |
|---|---|---|---|
| 快进合并 | 目标分支无新提交 | 历史线性、零噪音 | 丢失合并痕迹 |
| 三方合并 | 需保留合并节点 | 发布可追溯 | 多一个合并提交 |
| 变基合并 | 个人分支追求整洁 | 历史最干净 | 改写哈希、不能用于公共分支 |

五、踩坑清单
把最容易卡住的三点列出来,每条给现象、原因、修复:
- 现象:合并后历史里找不到“合入”记录。 原因:默认走了快进,没产生合并提交。修复:用
git merge --no-ff 分支名强制保留节点。 - 现象:rebase 中途一堆冲突,处理到怀疑人生。 原因:feature 提交太多、和 main 改动重叠。修复:先
git rebase --abort退出,把大分支拆小,或改用 --no-ff merge。 - 现象:push 被拒,提示 non-fast-forward。 原因:本地和远程分支分了叉,需要先把远程改动合并进来再推。修复:
git pull --rebase对齐后再git push。
动手验证清单(照着做)
- 前置安装:先确认本机已安装 Git,运行
git --version检查版本;没装先去官网下载安装包,再配置好命令行环境。 - 配置身份:用
git config --global user.name和git config --global user.email配置提交身份,否则首次提交会直接报错。 - 操作命令:按上面的方案一或方案二,依次执行对应的 merge 命令。
- 预期结果(未在本机执行):
git log --oneline --graph能看到两条分支的提交汇合;快进时是一条直线,三方合并会多出一个 merge 提交。 - 运行验证:
git status检查工作区是否干净,确认没有未提交的改动残留。 - 失败处理:若出现冲突,按提示打开冲突文件、保留正确内容后
git add再git commit完成合并;想反悔可用git merge --abort回到合并前。 - 修复补丁:提交信息写错就走 amend 改掉,分支选错就
git reset --hard ORIG_HEAD撤销刚才的合并动作。
总结
Git 合并分支的三种写法对应三种“历史观”:快进合并追求极简线性、三方合并保留可追溯的合并节点、变基合并产出最干净的线性历史。记住一条红线——已经推送到远程的公共分支不要 rebase,因为它会改写提交哈希、影响协作者。按团队约定选:个人分支用 rebase 整理,集成分支用 --no-ff merge 留痕。把这张对比表存好,下次合并分支就不会在 merge 和 rebase 之间纠结。
要点带走:
- 能快进时默认是快进,想留痕就加 --no-ff;
- 三方合并生成独立合并提交,适合集成分支;
- rebase 只用于未公开的本地分支,公共分支禁用。
下一步可以把分支策略与冲突处理系统学一遍。
延伸学习
想把 Git 合并与版本控制系统补齐,可以按这个顺序来:
- 遇到撤销与回退场景,翻 Git 版本回退方法 看 reset / revert 的实际用法;
- 想搞清最后一次提交怎么撤,Git 撤销提交方法 讲得很细;
- 想边学边练,编程课程 里有配套图文微课,适合巩固。
常见问题
Q:fast-forward 和 --no-ff 到底差在哪?
A:fast-forward 只是把指针往前挪,不产生新提交,历史是直线;--no-ff 会额外生成一个合并提交,把“两条线合过”这件事永久记下来。要可追溯就选后者。
Q:rebase 和 merge 该用哪个?
A:个人还没推送的分支用 rebase 让历史更整洁;已经公开、别人基于它工作的分支用 merge,避免改写哈希引发协作混乱。
Q:合并出现分支冲突怎么办?
A:冲突时 Git 会在文件里用 <<<<<<< 标出两边内容。手动改掉冲突标记、保存后执行 git add 文件名 与 git commit 即可完成合并提交。

TRAE-AI编程



