11、Git历史修改:git rebase(变基操作)、交互式rebase(合并提交、修改提交信息、调整提交顺序)

说到 Git 的历史修改,很多人第一反应是「历史还能改?那不是造假吗?」。嗯,我刚开始也有这个顾虑。后来在团队里踩过几次坑才明白——修改历史不是造假,而是让历史更清晰。你想想看,一个项目几百次提交,如果全是「fix bug」「update」「wip」这种信息,回头看的时候谁受得了?

今天我们就来聊聊 git rebase 这个神器。它有两个核心用途:一是变基,二是交互式重写历史。我个人习惯把 rebase 理解为「给提交历史做一次整理手术」——该合并的合并,该调整的调整,该改名的改名。

11.1 什么是变基(rebase)?

先说说变基。说白了,就是把一个分支的提交「移植」到另一个分支的顶端。

举个例子:你从 master 拉了个 feature 分支,开发了三天。这三天里 master 被别人推进了 5 个新提交。这时候你的 feature 分支就「落后」了。传统做法是用 merge,但 merge 会留下一个合并提交,历史看起来像一团乱麻。

rebase 的做法是:把你的 feature 分支上的提交,一个一个地「摘下来」,然后接到 master 的最新提交后面。这样历史就是一条直线,干净利落。

# 当前在 feature 分支
git rebase master

这条命令执行后,feature 分支的基底就从旧的 master 变成了最新的 master。我在项目中遇到过好几次这种情况——用 rebase 代替 merge,代码审查的时候 reviewer 都说「这历史看着真舒服」。

核心区别:
  • merge:保留所有分支历史,产生合并提交
  • rebase:重写历史,形成线性提交链
⚠️ 重要提醒: 永远不要对已经推送到远程且被其他人拉取的分支执行 rebase!因为 rebase 会重写提交的 SHA-1 值,别人再拉取时会冲突到怀疑人生。我曾经因为这个被同事「友好问候」过……

11.2 交互式 rebase:真正的历史手术刀

如果说普通 rebase 是自动移植,那交互式 rebase 就是手动精调。它让你可以:

  • 合并多个提交为一个
  • 修改提交信息
  • 调整提交顺序
  • 删除某个提交
  • 拆分一个提交为多个

启动交互式 rebase 的命令是:

git rebase -i HEAD~3

这个 HEAD~3 表示「从当前提交往前数 3 个提交」。执行后会打开一个编辑器,里面是这样的:

pick a1b2c3d 第一次提交
pick e4f5g6h 第二次提交
pick i7j8k9l 第三次提交

# Commands:
# p, pick = 使用该提交
# r, reword = 使用该提交,但修改提交信息
# s, squash = 合并到前一个提交
# f, fixup = 合并到前一个提交,丢弃提交信息
# d, drop = 删除该提交
# ...

你只需要把 pick 改成对应的命令就行。改完保存退出,Git 就会按你的指令重写历史。

11.3 合并提交(squash)

这是我最常用的操作。开发一个功能时,我习惯频繁提交——写一行代码就 commit 一次,方便随时回退。但推送到远程前,我会把这些零碎的提交合并成一个完整的提交。

比如我有三个提交:

pick a1b2c3d 添加登录功能-初步
pick e4f5g6h 添加登录功能-修复bug
pick i7j8k9l 添加登录功能-完善样式

我想把它们合并成一个,只需要把后两个的 pick 改成 squash

pick a1b2c3d 添加登录功能-初步
squash e4f5g6h 添加登录功能-修复bug
squash i7j8k9l 添加登录功能-完善样式

保存退出后,Git 会打开另一个编辑器让你写新的提交信息。我一般会写一个概括性的描述,比如「feat: 添加登录功能」。这样远程仓库里就只有一个干净的提交,而不是一堆「修修补补」的记录。

💡 我的习惯: 本地开发时随便提交,推送到远程前用交互式 rebase 整理。这样既保留了本地开发的安全性,又保证了远程历史的整洁。

11.4 修改提交信息(reword)

有时候提交信息写错了,或者写得太随意。比如我经常写「fix bug」这种毫无信息量的信息。这时候可以用 reword 来修改。

在交互式 rebase 界面中,把对应提交的 pick 改成 reword

reword a1b2c3d 修复用户无法登录的问题
pick e4f5g6h 添加密码重置功能

保存退出后,Git 会为每个标记为 reword 的提交打开编辑器,让你输入新的提交信息。改完一个继续下一个,直到全部改完。

我记得有一次,团队要求所有提交信息必须符合 Conventional Commits 规范。我对着几十个历史提交一个个改……嗯,从那以后我写提交信息就再也不敢偷懒了。

11.5 调整提交顺序

交互式 rebase 的另一个妙用是调整提交顺序。你只需要在编辑器中把行上下移动就行。

比如原始顺序是:

pick a1b2c3d 添加用户注册功能
pick e4f5g6h 添加用户登录功能
pick i7j8k9l 添加用户注销功能

我想把登录功能放在注册前面(虽然这不太合理,但假设有依赖关系),只需要把第二行剪切到第一行前面:

pick e4f5g6h 添加用户登录功能
pick a1b2c3d 添加用户注册功能
pick i7j8k9l 添加用户注销功能

保存退出,Git 就会按新顺序重新应用提交。不过要注意:如果提交之间有代码依赖,调整顺序可能导致冲突。这时候就需要手动解决冲突了。

⚠️ 冲突处理: 交互式 rebase 过程中如果遇到冲突,Git 会暂停。解决冲突后执行 git rebase --continue 继续。如果想放弃这次 rebase,执行 git rebase --abort 回到原始状态。

11.6 知识体系图

下面这张图展示了本章的核心知识结构:

Git Rebase 知识体系 git rebase 普通变基(git rebase master) 将当前分支提交移植到目标分支顶端 交互式 rebase(git rebase -i) 精细控制每个提交的处理方式 合并提交(squash) 将多个提交合并为一个 修改提交信息(reword) 更改提交的描述文字 调整提交顺序 重新排列提交的先后次序 删除提交(drop) 移除不需要的提交 ⚠️ 不要对已推送且被他人拉取的分支执行 rebase 冲突时:git rebase --continue / --abort

11.7 实战中的避坑指南

讲了这么多,最后分享几个我踩过的坑:

  • 不要 rebase 公共分支:我曾经在团队项目里 rebase 了 develop 分支,结果其他同事拉取时全乱了。从那以后我定了个规矩——只有自己的功能分支才能 rebase。
  • rebase 前先备份:可以用 git branch backup-feature 创建一个备份分支。万一 rebase 搞砸了,还能切回去重来。
  • 交互式 rebase 时看清范围HEAD~3 表示最近 3 个提交。如果写成了 HEAD~30,那就要处理 30 个提交,中间可能冲突不断。我建议一次只处理 5-10 个提交,分批进行。
  • rebase 后强制推送要谨慎:如果分支只有你一个人用,git push --force 没问题。但如果多人协作,用 git push --force-with-lease 更安全——它会检查远程分支是否被其他人更新过。
一句话总结: rebase 是整理本地提交历史的利器,但用在公共分支上就是灾难。记住「本地随便改,远程要谨慎」这个原则,你就能用好它。

好了,关于 rebase 的内容就聊到这里。交互式 rebase 是个需要多练的技能,刚开始可能会觉得麻烦,但用顺手了就会发现——它真的是保持 Git 历史整洁的最佳工具。

公众号:蓝海资料掘金营,微信 deep3321