文章目录
- [1. 简介](#1. 简介)
- [2. 格式](#2. 格式)
- [3. 选项](#3. 选项)
- [4. 示例](#4. 示例)
-
- [4.1 基础变基](#4.1 基础变基)
- [4.2 交互式变基(修改历史)](#4.2 交互式变基(修改历史))
- [4.3 变基到指定分支(非上游)](#4.3 变基到指定分支(非上游))
- [4.4 解决冲突](#4.4 解决冲突)
- [5. 注意](#5. 注意)
-
- [5.1 `git rebase` vs `git merge`](#5.1
git rebasevsgit merge) - [5.2 变基风险](#5.2 变基风险)
- [5.3 变基后如何推送?](#5.3 变基后如何推送?)
- [5.4 常用别名设置](#5.4 常用别名设置)
- [5.5 分支合并选择 rebase 还是 merge?](#5.5 分支合并选择 rebase 还是 merge?)
- [5.1 `git rebase` vs `git merge`](#5.1
- [6. 小结](#6. 小结)
- 参考文献
1. 简介
git rebase 用于在另一个基点上重新应用提交(reapply commits)。
rebase 名为变基,可以直接理解为改变基底,即将一系列提交按照原有次序依次应用到新的基底,使提交历史保持线性整洁。
核心对比:
git merge:创建合并提交,保留完整历史(会有分叉)。git rebase:不创建合并提交,历史变为一条直线。
典型场景:
- 将功能分支的提交"移动"到最新的
main分支上,使提交历史整洁。 - 交互式变基 (
-i) 用于合并、修改或删除提交。
2. 格式
bash
git rebase [-i | --interactive] [<options>] <upstream> [<branch>]
git rebase (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
3. 选项
bash
-i, --interactive
交互式变基,可以修改、合并、删除提交
--continue
解决冲突后继续变基
--skip
跳过当前提交(丢弃该提交的改动)
--abort
放弃变基,恢复到变基前的状态
--quit
放弃变基,但保留当前工作区和暂存区状态
--onto <newbase>
将提交变基到另一个基底(而非 <upstream>)
--root
变基所有提交(包括第一个)
--committer-date-is-author-date
保持提交时间不变(常用于迁移仓库)
4. 示例
4.1 基础变基
将当前 feature 分支的提交,移动到 main 分支的最新提交之后,使 feature 分支的基底从原来的位置更新为 main 的最新状态。
bash
git switch feature
git rebase main
变基前:
A---B---C feature
/
D---E---F---G main
变基后:
A'---B'---C' feature
/
D---E---F---G main
4.2 交互式变基(修改历史)
git rebase -i <commitid> 可以进行交互式变基,相比于 git rebase <branch> 用来变基,它经常用来操作当前分支的提交,git 会将 <commitid>-HEAD 之间的提交列在一个变基脚本中,每一笔提交根据用户设置的命令,会进行不同的操作,如修改提交信息、移除指定提交、合并两个提交为一个(压缩提交)、拆分提交等。
如要对最近 4 次提交进行重新操作,则:
bash
git rebase -i HEAD~4
1 pick af98479 [Description]:test4
2 pick 3cc9d66 test3
3 pick a7e819e usera commit03 branch2
4 pick efc5b15 usera commit04 branch2
5
6 # Rebase de7b118..efc5b15 onto de7b118 (4 command(s))
7 #
8 # Commands:
9 # p, pick = use commit
10 # r, reword = use commit, but edit the commit message
11 # e, edit = use commit, but stop for amending
12 # s, squash = use commit, but meld into previous commit
13 # f, fixup = like "squash", but discard this commit's log message
14 # x, exec = run command (the rest of the line) using shell
15 # d, drop = remove commit
16 #
17 # These lines can be re-ordered; they are executed from top to bottom.
18 #
19 # If you remove a line here THAT COMMIT WILL BE LOST.
20 #
21 # However, if you remove everything, the rebase will be aborted.
22 #
23 # Note that empty commits are commented out
这里我们可以修改pick为下面给出的其他命令,比如如果要修改提交信息,就使用r或reword。
各指令含义如下:
p,pick:直接使用该次提交
r,reword:使用该次提交,但重新编辑提交信息
e,edit:停止到该次提交,通过`git commit --amend`追加提交,完毕之后不要忘记使用`git rebase --continue`完成这此rebase
s,squash:压缩提交,将和上一次提交合并为一个提交
x,exec:运行命令
d,drop:移除这次提交
4.3 变基到指定分支(非上游)
--onto <newbase> 用于指定变基的新基底,即要把提交移动到哪里。它允许你将一段特定的提交应用到另一个分支上,而不是当前分支的上游。
git rebase --onto <newbase> <oldbase> [<branch>]
<newbase>:新基底(要搬到的目标位置)<oldbase>:旧基底(不包含这个提交及之前的提交)<branch>:要操作的分支(可选),缺省为当前分支
假设历史如下:
H---I---J feature
/
A---B---C---D---E main
\
F---G bugfix
场景: 只想把 feature 分支上的提交 I、J 搬到 main 上,不包括 H。
bash
git rebase --onto main H feature
结果:
A---B---C---D---E---I'---J' feature
- I、J 被复制到 main 后面
- H 没有被带过来
- bugfix 分支不受影响
4.4 解决冲突
在 rebase 的过程中,有可能会出现文件冲突。这种情况下,首先要解决冲突,解决冲突后可以选择继续执行或者结束 rebase。
bash
# 变基时产生冲突
git rebase main
# CONFLICT in main.go
# 手动解决冲突后
git add main.go
git rebase --continue
# 或者跳过当前提交
git rebase --skip
# 或者放弃变基
git rebase --abort
5. 注意
5.1 git rebase vs git merge
比如基于 main 分支创建了一个新的分支 experiment,开发任务分叉到两个不同分支,又各自提交了更新,那么提交历史日志如下:

现将 experiment 分支合并到 master 分支有两种方式,一是 git rebase,二是 git merge。
(a)merge 方式
切换到 main 分支,执行合并操作,会将 experiment 分支的最新快照 C3 合并到 master 分支的最新快照 C2 中并生成一个新的快照(并提交)。提交历史将出现分叉,示意如下:

操作命令如下:
shell
git checkout main
git merge experiment
(b)rebase 方式
git rebase 实现原理是首先找到两个分支的最近共同祖先 C1,提取 experiment 分支相对于该祖先的历次提交存为临时的差异补丁 patch 文件,存在 .git/rebase 目录下;然后在 master 分支最新提交 C2 的基础上,将保存的 patch 文件中的提交依次应用到 master 分支,并生成新的提交(commit id)。
最后回到 master 分支,进行一次快进合并,即将 experiment 分支合并到 master 分支。

操作命令如下:
shell
# 第一步,变基
git switch experiment
git rebase main
# 或者
git rebase main experiment
# 第二步,合并
git switch main
git merge experiment
5.2 变基风险
不要对已经推送到公共仓库的分支执行
git rebase!
因为变基会改写提交历史(生成新的提交 ID),如果其他人已经基于原分支开发,会导致混乱。
变基的实质是丢弃一些现有提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至远程仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,相当于删除之前的提交。你的同伴再次 git pull 时,会将存放在本地的你已经删除的提交再次合并,如果你的同伴将合并后的提交推送到服务器上,实际上是将那些已经被你变基抛弃的提交又恢复了回来,这会令人感到混乱。
安全做法:
- 个人开发分支:可以放心变基
- 公共分支(如
main、develop):禁止变基
5.3 变基后如何推送?
bash
# 方式一:使用 --force-with-lease(推荐)
git push --force-with-lease origin feature
# 方式二:使用 -f(强制覆盖,风险较高)
git push -f origin feature
5.4 常用别名设置
bash
git config --global alias.rb "rebase"
git config --global alias.rbi "rebase -i"
git config --global alias.rbc "rebase --continue"
# 使用别名
git rb main
git rbi HEAD~3
git rbc
5.5 分支合并选择 rebase 还是 merge?
变基和合并都可以完成分支合并,到底哪种方式更好?在回答这个问题之前,让我们退后一步,讨论一下提交历史的意义。
有一种观点认为,仓库的提交历史记录实际发生过什么。它是针对历史的文档,本身就有价值,不能乱改。 从这个角度看来,改变提交历史是一种亵渎,你使用谎言掩盖了实际发生过的事情。如果提交历史是一团糟怎么办? 既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。
另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。 持这一观点的人会使用 rebase 及 filter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。
现在,让我们回到之前的问题上来,到底 merge 还是 rebase 好?这并没有一个标准答案。Git 是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同,按实际需要来选择即可。
个人建议,merge 足以完成分支合并,易于理解,非必要补使用变基。
6. 小结
| 需求 | 命令 | 说明 |
|---|---|---|
| 将当前分支变基到 main | git rebase main |
保持线性历史 |
| 交互式修改最近 3 个提交 | git rebase -i HEAD~3 |
合并、修改、删除提交 |
| 解决冲突后继续 | git add . && git rebase --continue |
继续变基 |
| 放弃变基 | git rebase --abort |
恢复到变基前状态 |
| 变基到指定分支 | git rebase --onto newbase oldbase branch |
精细控制变基范围 |
一句话总结 :git rebase 保持提交历史线性整洁,适合个人分支;但公共分支禁止变基 (会改写历史,影响他人)。交互式变基 (-i) 可用来合并、修改、删除提交。变基后需用 --force-with-lease 推送。