跳到主要内容

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-forwardgit rebase/git pull --rebase 会先把当前分支的提交移动到目标分支之后,使其具备 fast-forward 条件,然后再前移指针,因此历史看起来是一条直线。如果 rebase 前有未提交的修改,需要借助 git stash 暂存。

Merge (No Fast-forward)

no-fastforward

最常被称为“保留分叉”的模式:无论本来能不能直接前进,强制额外生成一个 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

squash-commit

把整条分支“压成一个提交”的思路:在 PR 里看到了 20 个试验性质的 commit,只想让主干留下一个“功能完成”即可。

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

Rebase and Fast-forward

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

semi-linear

也叫“rebase 之后再 merge”:既想减少交错线,又想保留一次合并的记录(例如证明 PR 走过审批流程)。

  • 操作:Azure DevOps 中的 “Semi-linear merge” 等价于“先 rebase 后 merge”。在 Git 可用:

    git checkout feature/xxx
    git fetch origin target
    git rebase origin/target
    git checkout target
    git 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主干只有一条线,噪音少
线性历史、方便 bisectRebase + 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,以确保提交历史符合约定。