Git 合并策略怎么选:merge、rebase 与 squash 的取舍
同一个功能分支合回主干,团队里往往有三种声音:直接 merge、rebase 后再合、压成一个提交。争到最后常常变成风格之争,其实这三种选择回答的是同一个问题——你希望这段历史以后被人怎么读。
一、merge:保留分叉,历史最诚实
git switch main
git merge --no-ff feature/login
--no-ff 强制生成一个合并提交,即使可以快进也照样留痕。好处是分叉结构完整:能一眼看出这五个提交属于同一个功能,出问题时整段回退只要 git revert -m 1 <merge-sha>。
代价是提交图会变复杂。如果团队习惯小步频繁合并,主干历史会像地铁线路图,git log --oneline 基本失去可读性。
适合:功能分支生命周期长、需要审计留痕、多人并行开发同一模块。
二、rebase:线性重放,历史最干净
git switch feature/login
git rebase main
git switch main
git merge --ff-only feature/login
rebase 把分支上的提交一个一个重新应用到新基点上,每个提交都会生成新的 SHA。结果是主干一条直线,git log 读起来像按时间写好的变更日记。
有两个必须知道的副作用:
- SHA 变了。任何基于旧 SHA 的引用都会失效——别人如果已经拉过你的分支,他的历史会和你分叉
- 冲突可能反复出现。多个提交改同一处时,每一轮都要解一次
所以有条铁律:只 rebase 还没推送、或者只有你自己在用的分支。已经推送并且有人在用的分支做 rebase,等于逼所有人重来一遍。
三、squash:压成一个提交,历史最省事
git switch main
git merge --squash feature/login
git commit -m "feat(login): 支持短信验证码登录"
把分支上 N 个提交(包括一堆"修个 typo"“再试一次”)压成主干上的一个提交。主干历史干净到只剩有意义的节点。
代价是丢掉了开发过程的细节。想追溯"为什么当时这么改",只能回到 PR/MR 页面看讨论——所以 squash 的前提是:代码评审平台上的讨论记录要完整保留。
适合:分支里临时提交多、改动主题单一、团队只看主干历史。
四、怎么选
| 维度 | merge | rebase | squash |
|---|---|---|---|
| 历史形态 | 分叉 + 合并点 | 完全线性 | 线性,单点 |
| 提交粒度 | 保留全部 | 保留全部(新 SHA) | 只剩一个 |
| 回滚成本 | 低(整段 revert) | 中(逐个 revert) | 最低(一个提交) |
| 冲突处理 | 一次解决 | 可能多次 | 一次解决 |
| 对协作者影响 | 无 | 有,需协调 | 无 |
| 可追溯性 | 高 | 高 | 低(依赖 PR 记录) |
一个务实的组合是:本地开发用 rebase 跟进主干,合并时用 squash 落到主干。前者让你的分支不会落后到冲突爆炸,后者保证主干每个提交都对应一个完整的变更单元。
五、出问题怎么救
rebase 中途出错:
git rebase --abort # 完全放弃,回到 rebase 前
git rebase --continue # 解决冲突后继续
git rebase --skip # 跳过当前这个提交
真正救命的是 reflog。它记录 HEAD 的每一次移动,即使提交已经被 rebase 改写、分支被 reset 掉,只要还在本地仓库里就能捞回来:
git reflog
git reset --hard HEAD@{3}
养成习惯:做任何改写历史的操作前,先 git branch backup-$(date +%m%d)。这条命令花不到一秒,能省掉一整晚的排查。
小结
三种方式没有绝对优劣,只有适用场景不同。判断标准就一句话:这段历史将来主要是给机器看(回滚、bisect)还是给人看(理解为什么这么改)。给机器看,squash 最省心;给人看,merge 保留的上下文最多;想让主干始终可读,用 rebase 但务必只对自己拥有的分支动手。
- 点赞
- 收藏
- 关注作者
评论(0)