Git 分支合并的几种模式
合并分支时不只有“ 能合进去就好”。不同的合并模式会留下截然不同的提交拓扑,影响后续审计、回滚与分支管理。以下总结整理 Azure DevOps 中提供,也可以在纯 Git 操作中复现的四种主要模式,并给出适用场景。
两种底层形态:Fast-forward 与 No Fast-forward
所有合并动作最终都落在这两种形态之一:
-
Fast-forward (FF)
- 条件:目标分支(如
dev)没有新的提交,当前分支只是落后。 - 行为:Git 直接把当前分支指针前移到目标分支的最新提交,不生成新的 merge commit。
- 命令:默认
git merge在满足时会自动 fast-forward;git merge --ff-only可强制只接受此形态。 - 场景:纯同步、保持线性历史、自动化合并(机器人同步主干等)。
- 条件:目标分支(如
-
No Fast-forward (No-FF)
- 条件:两个分支各自有提交,无法只靠前移指针。
- 行为:Git 创建一个新的 merge commit,把两条历史接在一起,保留拓扑。
- 命令:
git merge --no-ff强制生成 merge commit,即使原本可以 fast-forward。 - 场景:需要记录“这个功能分支曾存在”或审计需要完整的父子关系。
Rebase 与 fast-forward:git rebase/git pull --rebase 会先把当前分支的提交移动到目标分支之后,使其具备 fast-forward
条件,然后再前移指针,因此历史看起来是一条直线。如果 rebase 前有未提交的修改,需要借助 git stash 暂存。
Merge (No Fast-forward)

最常被称为“保留分叉”的模式:无论本来能不能直接前进,强制额外生成一个 merge commit。需要注意,Git 默认行为是“能
fast-forward 就 fast-forward”,只有在显式传入 --no-ff 或配置 merge.ff=false 时才会进入此模式。
- 操作:
git merge --no-ff feature/xxx - 行为:无论是否可以 fast-forward,都创建一个新的 merge commit。提交图完整保留功能分支的分叉记录。
- 优点:明确地记录“此提交来自某个 PR/功能分支”,方便审计,也能还原出分支的开始与结束。
- 缺点:提交图会出现较多叉路,如果团队频繁合分支,图形会更复杂。
- 适用:团队需要保留 PR 痕迹,或主干 (main/master) 审计严格,必须知道每个功能分支的父子关系。
Squash Commit

把整条分支“压成一个提交”的思路:在 PR 里看到了 20 个试验性质的 commit,只想让主干留下一个“功能完成”即可。
- 操作:
git merge --squash feature/xxx && git commit - 行为:把功能分支的所有提交压缩成一个新提交,写入目标分支。原分支的中间提交不会留下 trace。
- 优点:目标分支历史干净;在 feature 分支中尝试过的冗余提交不会污染主分支。
- 缺点:丢失功能分支内部的细粒度历史;要追查细节只能回到 feature 分支或查 reflog。
- 适用:短期 feature、实验性开发,或希望 PR 对应主分支上的单一“功能汇总提交”。
Rebase and Fast-forward

“我只想让历史保持一条直线”时使用:先把功能分支搬到目标分支的最新提交之后,再用 fast-forward 串起来。
- 操作:
git rebase target && git checkout target && git merge --ff-only feature - 行为:先把功能分支 rebase 到目标分支的最新提交,再用 fast-forward 方式把提交串接进去,不产生 merge commit。
- 优点:历史完全线性;每个 commit 都紧挨着目标分支的历史,便于 bisect。
- 缺点:PR 痕迹会消失;多人协作时需要确保 rebase 后的提交未被他人依赖。
- 适用:个人维护的分支、自动化同步、多 feature 自我整合后再并入公共分支的场景。
Semi-linear Merge

也叫“rebase 之后再 merge”:既想减少交错线,又想保留一次合并的记录(例如证明 PR 走过审批流程)。
-
操作:Azure DevOps 中的 “Semi-linear merge” 等价于“先 rebase 后 merge”。在 Git 可用:
git checkout feature/xxxgit fetch origin targetgit rebase origin/targetgit checkout targetgit merge --no-ff feature/xxx -
行为:先 rebase,让 feature 的 commits 排在目标分支之后,再生成一个 merge commit。提交图比纯
--no-ff更整洁。 -
优点:既保留 PR/merge 记录,又能避免交错的提交图;适合 main ← develop 等重要分支合并。
-
缺点:需要在合并前执行 rebase,流程较长;rebase 中仍可能出现冲突。
-
适用:希望看到“这个 merge 提交来自 rebase 后的整洁分支”,并把 PR 记录下来。例如 develop 合并到 main。
选择建议
| 需求/考量 | 推荐模式 | 理由 |
|---|---|---|
| 完整记录 PR / 分支来源 | Merge (No FF) | 保留 merge commit,审计友好 |
| 主干历史保持简洁、功能以单提交落地 | Squash commit | 主干只有一条线,噪音少 |
| 线性历史、方便 bisect | Rebase + fast-forward | 没有 merge commit,调试简单 |
| 同时保留线性提交与 merge 记录 | Semi-linear merge | 先 rebase,再生成 merge commit |
团队可以根据 main/develop 等分支的重要性,配置不同默认模式。例如:feature → develop 用 squash;develop → main 用 semi-linear。Azure DevOps、GitHub 均可在仓库层级限制允许的 merge type,以确保提交历史符合约定。