10、Git rebase:变基的艺术与陷阱

说实话,Git 里面最容易让人迷惑的命令,rebase 绝对排前三。我刚开始用 Git 那会儿,看到 rebase 这个词就头大——什么叫「变基」?好好的分支为什么要动它的根?

后来在项目里踩了几次坑,才慢慢摸透它的脾气。今天我就把 rebase 的来龙去脉讲清楚,包括它和 merge 到底怎么选,交互式 rebase 怎么玩,以及那些你一定要注意的坑。

一句话总结:rebase 就是把一个分支的提交「移植」到另一个分支的最新提交之上,让历史变成一条直线。

10.1 rebase 到底是什么?

先看个场景。你从 master 拉了个 feature 分支,开始写代码。这时候 master 被别人推进了两个新提交。你的 feature 分支现在落后了。

用 merge 的话,会生成一个合并提交,历史变成分叉再合并的样子。用 rebase 呢?它会把你 feature 上的提交「摘下来」,放到 master 的最新提交后面。

说白了,rebase 就是重新设置你的「基准点」。原来你的基准是 master 的旧提交,现在改成最新的那个。

# 当前在 feature 分支
git rebase master

# 效果:feature 的提交被放到 master 最新提交之后
# 原来:A---B---C (master)
#       \
#        D---E (feature)
# 之后:A---B---C (master)
#               \
#                D'---E' (feature)

注意那个 D' 和 E'——它们是新的提交对象,虽然内容和原来一样,但 hash 变了。这意味着什么?嗯,后面我会讲,这是个关键点。

10.2 rebase 与 merge 的区别

这个问题几乎每个面试都会问到。我个人的理解是这样的:

对比维度 merge rebase
历史记录 保留完整的分叉和合并记录 线性历史,没有分叉
提交对象 原有提交不变,新增合并提交 生成全新的提交(hash 改变)
冲突处理 一次解决,合并提交时处理 每个提交都可能要解决冲突
适用场景 公共分支、长期分支 个人分支、整理历史
可追溯性 强,能看到谁合并了谁 弱,丢失了分支合并信息

我个人的习惯是:公共分支用 merge,个人分支用 rebase。为什么?因为 rebase 会改写历史,如果你在公共分支上 rebase,别人会疯掉的。

我的经验:有一次我在团队分支上用了 rebase,结果同事 pull 的时候报了一大堆冲突。从那以后我就记住了——已经推送到远程的分支,尤其是多人协作的,千万别 rebase。

10.3 交互式 rebase:整理历史的利器

交互式 rebase 是我最常用的功能之一。它让你在 rebase 的过程中,对每个提交进行操作——合并、修改、删除、重排,都可以。

# 对最近 3 个提交进行交互式 rebase
git rebase -i HEAD~3

# 会打开一个编辑器,内容类似:
# pick a1b2c3 第一个提交
# pick d4e5f6 第二个提交
# pick g7h8i9 第三个提交

# 你可以把 pick 改成:
# pick   - 保留这个提交
# reword - 修改提交信息
# edit   - 修改提交内容
# squash - 合并到上一个提交
# fixup  - 合并但丢弃提交信息
# drop   - 删除这个提交

举个例子。你写代码时经常「先提交再说」,结果提交信息全是「fix bug」「update」「wip」。等到提 PR 的时候,这些提交看着就很不专业。

这时候交互式 rebase 就派上用场了:

# 把最后 5 个提交合并成 1 个
git rebase -i HEAD~5

# 把除了第一个之外的 pick 改成 squash
# 然后统一写一个漂亮的提交信息

我曾经在一个项目里,把 20 多个零散的提交整理成了 3 个有意义的提交。PR 审核的人看了直呼舒服。

10.4 rebase 的注意事项

这里我要重点说几个坑,都是我自己踩过的。

警告:绝对不要对已经推送到远程的分支做 rebase!除非你确认只有你在用这个分支。

第一,rebase 后要强制推送。因为提交 hash 变了,普通的 git push 会被拒绝。你需要用 git push --force 或者更安全的 git push --force-with-lease。

# 推荐使用这个,它会检查远程分支是否有新的提交
git push --force-with-lease

第二,冲突可能多次出现。merge 是一次性解决所有冲突。rebase 是逐个提交应用,每个提交都可能产生冲突。如果你的分支有 10 个提交,可能要解决 10 次冲突。

我有个小技巧:如果冲突太多,可以先用 merge 合并一次,然后再 rebase。或者用 git rerere 功能自动记录冲突解决方案。

第三,rebase 会丢失分支的上下文。你想想看,rebase 之后,原来的分支分叉点就没了。别人看历史的时候,不知道这个功能分支是从哪里拉出来的。

第四,不要 rebase 已经打 tag 的提交。tag 是绑定在具体提交上的,rebase 之后提交变了,tag 就指向了旧提交,容易造成混乱。

我的建议:rebase 之前,先用 git branch backup-feature 创建一个备份分支。万一 rebase 出问题,还能切回去重来。

10.5 什么时候用 rebase,什么时候用 merge?

这个问题没有标准答案。我分享一下我的决策流程:

  • 个人开发分支:用 rebase,保持历史整洁
  • 多人协作分支:用 merge,保留协作信息
  • 准备提 PR 前:用 rebase 整理提交,让 PR 更好审核
  • 从主分支拉取更新:用 rebase,避免产生多余的合并提交
  • 修复紧急 bug:用 merge,快速解决问题,不折腾历史

说白了,rebase 和 merge 没有谁对谁错,只有合不合适。我见过有些团队完全禁止 rebase,也见过有些团队只用 rebase。关键是大家约定好规则,保持一致。

最后说一句:rebase 是个强大的工具,但强大也意味着危险。用之前想清楚,用的时候小心点,用完之后检查一下。嗯,这样基本就不会出大问题。

Git Rebase 核心知识体系 Git Rebase 核心概念 • 重新设置基准点 • 移植提交到新位置 • 生成全新提交对象 vs Merge • 线性历史 vs 分叉历史 • 个人分支 vs 公共分支 • 可追溯性弱 vs 强 交互式 Rebase • pick / reword / edit • squash / fixup / drop • 整理提交历史 注意事项 • 禁止对远程公共分支 rebase • 需要强制推送 (--force-with-lease) • 冲突可能多次出现 • 备份分支以防万一
公众号:蓝海资料掘金营,微信deep3321