git rebase

文章目录

  • [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 rebase vs git merge)
    • [5.2 变基风险](#5.2 变基风险)
    • [5.3 变基后如何推送?](#5.3 变基后如何推送?)
    • [5.4 常用别名设置](#5.4 常用别名设置)
    • [5.5 分支合并选择 rebase 还是 merge?](#5.5 分支合并选择 rebase 还是 merge?)
  • [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 时,会将存放在本地的你已经删除的提交再次合并,如果你的同伴将合并后的提交推送到服务器上,实际上是将那些已经被你变基抛弃的提交又恢复了回来,这会令人感到混乱。

安全做法:

  • 个人开发分支:可以放心变基
  • 公共分支(如 maindevelop):禁止变基

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 推送。


参考文献

Git - git-rebase Documentation

相关推荐
大志哥1236 小时前
idea+git插件+云备份实现项目新分支新建维护
git
恋喵大鲤鱼6 小时前
git merge
git·git merge
码客日记6 小时前
Spring Boot 配置文件敏感信息加密(Jasypt 企业级完整方案)
java·spring boot·git
_codemonster7 小时前
Codex 核心进阶玩法(技能/MCP/派生/分叉/Git/钩子/子智能体)
git
逻极7 小时前
Git 从入门到精通:版本控制协作实战指南
git·github·分支管理·版本控制
恋喵大鲤鱼7 小时前
git clean
git·git clean
Patrick_Wilson8 小时前
为省一次回归测试,该不该把多个改动堆进一条分支?
git·ci/cd·架构
恋喵大鲤鱼8 小时前
git blame
git·git blame
yeflx8 小时前
Git操作
git