"merge 还是 rebase?"几乎是每个 Git 使用者都会纠结的问题。两者都能把分支的改动合到一起,但哲学完全不同:merge 尊重历史,rebase 重写历史。本文讲清两者的本质区别与适用场景。
1. 本质区别:一棵树 vs 一条线
merge 会创建一个"合并提交",记录两条分支的汇合点,历史呈分叉的树状;rebase 则把你分支的提交"摘下来",重放到目标分支最新提交之后,历史变成一条直线。
# merge:保留分叉,生成合并提交
git checkout feature
git merge main
# rebase:把 feature 的提交重放到 main 之后
git checkout feature
git rebase main
2. 什么时候用 merge
- 合并公共分支:把 feature 合回 main 时,merge 保留真实开发脉络,方便回溯功能来源。
- 需要保留合并上下文:如发布分支的合并,评审和回滚都要看到汇合点。
- 你不想重写任何提交:merge 不改动已有提交的哈希,最安全。
git checkout main
git merge --no-ff feature/login # 强制生成合并提交,历史清晰
3. 什么时候用 rebase
- 同步主干的更新:功能分支开发到一半,想把 main 的新改动吸收进来,rebase 让分支保持线性,合并更干净。
- 整理自己的提交:提交太碎、尽是"fix typo",用交互式 rebase 合并成有意义提交。
- 追求线性历史:配合 GitHub 的 Squash 合并,main 上每个 PR 只留一个提交,用
git bisect定位问题很舒服。
# 同步 main 的最新改动
git fetch origin
git rebase origin/main
# 交互式整理最近 3 个提交
git rebase -i HEAD~3
# 编辑器里把 pick 改成 squash 即可合并提交
4. 交互式 rebase 实战
git rebase -i 是整理提交的利器,常用指令:
| 指令 | 作用 |
|---|---|
| pick | 保留该提交(默认) |
| reword | 保留提交,但修改提交信息 |
| squash | 合并到上一个提交,并合并提交信息 |
| fixup | 合并到上一个提交,丢弃该提交信息 |
| drop | 删除该提交 |
# 把 "fix typo"、"add tests" 合并进主提交
git rebase -i HEAD~4
# 编辑器里从上到下依次是:
# pick a1b2c3d feat: 实现登录
# fixup e4f5a6b fix typo
# fixup c7d8e9f add tests
5. 黄金法则:绝不要 rebase 公共分支
只 rebase 未推送过或只有你自己在用的分支。别人已基于你的提交开发时 rebase,会重写哈希,造成重复提交与合并地狱。
判断标准:已经 push 且可能有协作者拉取过的分支,不要再 rebase;已推送又必须整理,优先新建分支而不是重写。
6. 一张决策清单
- 要合入公共主干 → merge(最好
--no-ff) - 同步主干到自己的功能分支 → rebase
- 提交太碎想整理(未推送)→ rebase -i
- 已经推送过的分支 → 都不要动,用新提交修正
💡 学习建议:在本地建两个测试分支,分别用 merge 和 rebase 合并,再用 git log --graph --oneline 对比两者历史形状,一眼就能看出"树"与"线"的差别,之后选择就不再纠结。