Git + Gerrit 第五课:rebase 变基与提交历史整理

本节目标

这一节学习 Git 中非常重要、但新手容易害怕的命令:rebase

你要理解这些问题:

复制代码
什么是 rebase?
rebase 和 merge 有什么区别?
为什么 Gerrit 工作流里经常使用 rebase?
rebase 冲突怎么解决?
什么时候可以 rebase,什么时候不要 rebase?

本节重点命令:

复制代码
git fetch
git rebase
git rebase --continue
git rebase --abort
git rebase --skip
git log --oneline --graph --all

1. rebase 是什么

rebase 中文常翻译为:

复制代码
变基

这个名字听起来复杂,但它的核心思想很简单:

复制代码
把你的提交移动到另一个分支的最新提交后面。

假设一开始主分支是:

复制代码
A --- B --- C    main

你从 C 创建了功能分支:

复制代码
A --- B --- C    main
           \
            D --- E    feature/login

后来别人往 main 合入了新提交:

复制代码
A --- B --- C --- F --- G    main
           \
            D --- E          feature/login

这时你的功能分支已经落后于 main

如果执行:

复制代码
git switch feature/login
git rebase main

结果会变成:

复制代码
A --- B --- C --- F --- G    main
                     \
                      D' --- E'    feature/login

注意这里变成了 D'E'

这表示:

复制代码
Git 把你的 D、E 两个提交重新放到了 main 最新提交 G 的后面。

2. rebase 的直观理解

可以把 rebase 理解成:

复制代码
我先把自己的改动临时拿下来。
然后把分支更新到目标分支最新位置。
最后再把自己的改动一个一个重新放上去。

过程像这样:

复制代码
1. 找到你和目标分支的共同祖先
2. 暂时取下你自己的提交
3. 把当前分支移动到目标分支最新提交
4. 重新应用你自己的每一个提交

所以 rebase 之后,提交历史会更像一条直线。

3. merge 和 rebase 的区别

假设现在历史是:

复制代码
A --- B --- C --- F --- G    main
           \
            D --- E          feature/login

3.1 使用 merge

执行:

复制代码
git switch feature/login
git merge main

结果可能是:

复制代码
A --- B --- C --- F --- G
           \           \
            D --- E --- M    feature/login

这里会产生一个合并提交 M

特点:

复制代码
保留真实分支历史。
会多一个 merge commit。
历史图可能更复杂。

3.2 使用 rebase

执行:

复制代码
git switch feature/login
git rebase main

结果可能是:

复制代码
A --- B --- C --- F --- G --- D' --- E'    feature/login

特点:

复制代码
提交历史更直线。
不会产生额外 merge commit。
会重写你本地分支上的提交。

4. 为什么 Gerrit 经常使用 rebase

Gerrit 很重视每一个 Change 的评审质量。

很多 Gerrit 团队希望提交历史像这样:

复制代码
A --- B --- C --- D --- E --- F

而不是这样:

复制代码
A --- B --- C --- M --- N
     \       \     /
      D --- E ----

原因是:

  • 历史更清楚

  • 每个 commit 更容易评审

  • 不容易产生无意义的 merge commit

  • 一个 Change 对应一个清晰的修改

  • Gerrit 页面上的 Patch Set 更容易理解

所以在 Gerrit 中,经常会看到这种流程:

复制代码
git fetch origin
git switch feature/login
git rebase origin/master
git push origin HEAD:refs/for/master

如果目标分支是 develop

复制代码
git fetch origin
git switch feature/login
git rebase origin/develop
git push origin HEAD:refs/for/develop

5. git fetch 是什么

在 rebase 前,经常先执行:

复制代码
git fetch origin

它的作用是:

复制代码
从远程仓库获取最新提交信息,但不自动合并到当前分支。

git pull 的区别:

复制代码
git fetch = 只下载远程更新,不动当前工作区
git pull  = fetch + merge 或 fetch + rebase

新手阶段建议多用:

复制代码
git fetch

再明确决定:

复制代码
git merge origin/master

或者:

复制代码
git rebase origin/master

这样你更清楚 Git 正在做什么。

6. rebase 的标准流程

假设你在 feature/login 分支开发,目标分支是 master

标准流程:

复制代码
git status
git fetch origin
git switch feature/login
git rebase origin/master

如果没有冲突,rebase 会直接完成。

然后你可以查看历史:

复制代码
git log --oneline --graph --all

最后推送到 Gerrit:

复制代码
git push origin HEAD:refs/for/master

7. rebase 时发生冲突怎么办

rebase 本质上是把你的提交一个一个重新应用到目标分支后面。

如果某个提交和目标分支冲突,Git 会停下来,让你解决。

你可能看到:

复制代码
CONFLICT (content): Merge conflict in readme.md
error: could not apply abc1234... 修改登录说明

意思是:

复制代码
Git 正在重新应用某个提交时发生冲突。
你需要解决冲突后继续 rebase。

标准流程:

复制代码
git status

# 手动修改冲突文件

git add readme.md
git rebase --continue

注意:

复制代码
rebase 冲突解决后,不是 git commit。
而是 git rebase --continue。

这是 rebase 和 merge 冲突处理的重要区别。

8. rebase 冲突解决流程

完整流程:

复制代码
git rebase origin/master

# 如果出现冲突
git status

# 打开冲突文件,删除冲突标记,整理成最终正确内容

git add 冲突文件
git rebase --continue

如果后面还有其他提交冲突,Git 可能会再次停下来。

你继续重复:

复制代码
git status
# 解决冲突
git add 冲突文件
git rebase --continue

直到 rebase 完成。

9. 放弃 rebase

如果 rebase 过程中发现问题太复杂,想回到 rebase 前的状态,可以执行:

复制代码
git rebase --abort

它的作用是:

复制代码
取消当前 rebase,回到 rebase 开始之前。

适合这些场景:

  • rebase 了错误的目标分支

  • 冲突太多,想重新整理

  • 不确定怎么解决

  • 想先备份当前分支

新手遇到不确定情况时,git rebase --abort 是一个很重要的安全按钮。

10. 跳过当前提交

rebase 过程中还有一个命令:

复制代码
git rebase --skip

它的作用是:

复制代码
跳过当前正在应用的提交。

注意:

复制代码
这个命令可能会丢掉当前提交的修改。
新手不要随便使用。

只有当你非常确定这个提交已经不需要了,才使用 --skip

11. rebase 会重写提交历史

这是 rebase 最重要的风险点。

rebase 后,原来的提交:

复制代码
D --- E

会变成:

复制代码
D' --- E'

虽然代码内容可能一样,但 commit id 已经变了。

原因是 commit id 和这些信息有关:

  • 提交内容

  • 父提交

  • 作者信息

  • 时间信息

  • commit message

rebase 改变了提交的父节点,所以 commit id 会变化。

所以要记住:

复制代码
rebase 会重写提交历史。

12. 什么时候可以 rebase

通常可以 rebase 的情况:

复制代码
你的本地功能分支
还没有被别人基于它继续开发
还没有作为公共分支给别人使用
你只是想把自己的提交更新到目标分支最新位置

例如:

复制代码
git switch feature/login
git rebase origin/master

这是常见且合理的。

13. 什么时候不要随便 rebase

不要随便 rebase 这些分支:

复制代码
main
master
develop
release
多人共享分支
已经推送并被别人基于其开发的分支

原因是:

复制代码
rebase 会改变提交历史。
如果别人已经基于旧历史开发,你重写历史会让别人很痛苦。

简单记忆:

复制代码
自己的本地分支可以 rebase。
公共共享分支不要随便 rebase。

14. Gerrit 中的 rebase 和 Patch Set

在 Gerrit 中,一个 Change 通常依靠 Change-Id 识别。

如果你 rebase 后重新推送:

复制代码
git push origin HEAD:refs/for/master

只要 commit message 里的 Change-Id 没变,Gerrit 通常会把它识别为同一个 Change 的新 Patch Set。

例如:

复制代码
实现登录功能

Change-Id: Iabc1234567890abcdef1234567890abcdef1234

rebase 后仍然保留这个 Change-Id,推送后会生成新的 Patch Set,而不是新的 Change。

15. rebase 后推送失败怎么办

如果你 rebase 过一个已经推送到普通远程分支的分支,再执行:

复制代码
git push

可能会失败。

因为远程分支历史和你本地历史不一致。

普通 Git 平台有时需要:

复制代码
git push --force-with-lease

但注意:

复制代码
不要随便 force push。
尤其不要对 main/master 使用 force push。

在 Gerrit 工作流里,你通常不是推送到普通分支,而是推送到:

复制代码
git push origin HEAD:refs/for/master

所以很多时候不需要直接 force push 到目标分支。

16. merge 与 rebase 怎么选择

可以这样理解:

场景 推荐
想保留完整分支合并历史 merge
想让提交历史更线性 rebase
本地个人功能分支同步主分支 rebase 常见
公共共享分支整合功能 merge 常见
Gerrit 单 Change 更新目标分支 rebase 常见

简单记忆:

复制代码
merge:把别人的历史合进来。
rebase:把自己的提交搬到别人最新历史后面。

17. 实战练习:体验 rebase

创建练习仓库:

复制代码
mkdir git-rebase-demo
cd git-rebase-demo
git init

创建初始提交:

复制代码
echo "base" > readme.txt
git add readme.txt
git commit -m "添加基础文件"

创建功能分支:

复制代码
git switch -c feature/login
echo "login feature" > login.txt
git add login.txt
git commit -m "添加登录功能"

切回主分支:

复制代码
git switch main

如果你的主分支叫 master

复制代码
git switch master

在主分支添加新提交:

复制代码
echo "main update" >> readme.txt
git add readme.txt
git commit -m "更新主分支说明"

查看历史:

复制代码
git log --oneline --graph --all

切回功能分支并 rebase:

复制代码
git switch feature/login
git rebase main

如果主分支叫 master

复制代码
git rebase master

再次查看历史:

复制代码
git log --oneline --graph --all

你会看到功能分支的提交被移动到了主分支最新提交后面。

18. 实战练习:制造 rebase 冲突

创建练习仓库:

复制代码
mkdir git-rebase-conflict-demo
cd git-rebase-conflict-demo
git init

创建初始文件:

复制代码
echo "version: base" > config.txt
git add config.txt
git commit -m "添加基础配置"

创建功能分支:

复制代码
git switch -c feature/login
echo "version: login feature" > config.txt
git add config.txt
git commit -m "修改登录配置"

切回主分支:

复制代码
git switch main

如果你的主分支叫 master

复制代码
git switch master

主分支也修改同一行:

复制代码
echo "version: main update" > config.txt
git add config.txt
git commit -m "修改主分支配置"

切回功能分支:

复制代码
git switch feature/login

执行 rebase:

复制代码
git rebase main

如果主分支叫 master

复制代码
git rebase master

这时应该会出现冲突。

19. 实战练习:解决 rebase 冲突

查看状态:

复制代码
git status

打开 config.txt,可能看到:

复制代码
<<<<<<< HEAD
version: main update
=======
version: login feature
>>>>>>> 修改登录配置

手动改成最终正确内容,例如:

复制代码
version: main update with login feature

然后执行:

复制代码
git add config.txt
git rebase --continue

如果还有冲突,继续解决。

如果完成,查看历史:

复制代码
git log --oneline --graph --all

20. rebase 和 amend 的关系

Gerrit 中常见组合是:

复制代码
git rebase origin/master
git commit --amend
git push origin HEAD:refs/for/master

但顺序要根据场景判断。

如果只是根据评审意见修改当前 Change:

复制代码
git add .
git commit --amend
git push origin HEAD:refs/for/master

如果目标分支已经更新,你需要先同步:

复制代码
git fetch origin
git rebase origin/master
git push origin HEAD:refs/for/master

如果 rebase 后还要修改提交信息或整理代码,再使用:

复制代码
git commit --amend

核心原则:

复制代码
保持 Change-Id 不变。
保持提交内容清晰。
不要制造无意义的 merge commit。

21. 常见错误

错误一:在公共分支上 rebase

不要随便对这些分支做 rebase:

复制代码
main
master
develop
release

尤其不要 rebase 后强推公共分支。

错误二:rebase 冲突后执行 git commit

rebase 冲突解决后应该执行:

复制代码
git rebase --continue

不是:

复制代码
git commit

错误三:不确定时乱用 --skip

git rebase --skip 可能丢掉当前提交。

新手不要随便用。

错误四:rebase 后 Change-Id 丢失

Gerrit 依赖 Change-Id 判断是否是同一个 Change。

如果 Change-Id 丢了,可能会生成新的 Change。

错误五:rebase 前工作区不干净

rebase 前先看:

复制代码
git status

最好保证:

复制代码
nothing to commit, working tree clean

22. 本节必须记住的命令

复制代码
git fetch origin
git rebase origin/master
git rebase --continue
git rebase --abort
git rebase --skip
git log --oneline --graph --all

对应含义:

复制代码
git fetch origin                  获取远程最新信息
git rebase origin/master          把当前分支提交搬到 origin/master 后面
git rebase --continue             解决冲突后继续 rebase
git rebase --abort                放弃本次 rebase
git rebase --skip                 跳过当前提交
git log --oneline --graph --all   查看提交历史图

23. 本节总结

这一节你学习了 rebase

  • rebase 是把你的提交重新放到目标分支最新提交后面

  • rebase 可以让提交历史更直线

  • merge 会保留合并历史,rebase 会重写本地提交

  • Gerrit 中经常使用 rebase 来保持 Change 清晰

  • rebase 冲突解决后要用 git rebase --continue

  • 不确定时可以用 git rebase --abort

  • 不要随便 rebase 公共分支

  • 不要随便 force push 公共分支

最重要的一句话:

复制代码
自己的本地功能分支可以 rebase,公共共享分支不要随便 rebase。

下一节建议学习:

复制代码
Git commit --amend、Gerrit Patch Set 与 Change-Id:如何根据评审意见更新同一个 Change。
相关推荐
柚鸥ASO优化15 小时前
微信正在变成“搜索引擎”:小程序SEO机会全面爆发
搜索引擎·微信·小程序·小程序优化
醉颜凉15 小时前
Lucene底层原理:倒排索引实现原理与代码实战,彻底吃透搜索引擎核心
搜索引擎·mybatis·lucene
飞火流星0202715 小时前
【最佳实践】TDengine 3.3.6.13安装---RPM包安装、开源版本下载、TDengine基本操作
大数据·时序数据库·tdengine
海兰15 小时前
手把手elasticsearch学习之构建 HITL AI 代理
人工智能·学习·elasticsearch
宽海智能仓储物流15 小时前
从状态检查到数据备份:仓储PLC控制器保养周期与实操清单
大数据·数据仓库·自动化
love8888_cnsd15 小时前
Git & Linux 速查表
java·linux·git·后端·elasticsearch
AI周红伟16 小时前
中国第一大DRAM,长鑫科技,迈向算力第二巨头
大数据·人工智能·科技·elasticsearch·搜索引擎
运维行者_20 小时前
Applications Manager中的Redis监控
大数据·服务器·数据库·人工智能·网络协议
Agent手记1 天前
跨境电商如何用AI Agent自动运营多平台店铺?企业级「龙虾」矩阵智能体全流程落地指南
大数据·人工智能·ai·矩阵