本篇博客的视频教程首发于 Youtube:科技小飞哥,加入 电报粉丝群 获得最新视频更新和问题解答。

背景

在你看这篇文章之前,我假设你已经至少会git的一些基本指令,能进行简单的git repo的操作。比如:pull, add, commit, merge, push 等几个基础命令至少会用。而我们刚开始接触git的时候,会这些简单的指令能满足基本的git代码的开发与提交。

但是当你把git作为一个常用的工具之后,还是需要了解一些相对进阶的常用指令。而这篇文章我们来讲讲进阶命令git rebase

理解Rebase命令

git rebase 命令的文档描述是:

Reapply commits on top of another base tip

从字面上理解是在另一个基端之上重新应用提交,这个定义听起来有点抽象,换个角度可以理解为将分支的基础从一个提交改成另一个提交,使其看起来就像是从另一个提交中创建了分支一样

下面我来详细讲解一下git rebase命令最常用的两个用法,让大家更深入的理解 在另一个基端之上重新应用提交 的概念。

Rebase常用的两个使用场景是:

  • 合并commit记录
  • 合并分支(重写提交历史)

一、合并commit记录

有时候开发一个 feature 的时候,在自己的 feature 分支有过多次修改记录,多的时候有好几十次。每次都进行了一些小的修改。这么多commit看起来很不舒服,而且如果合并到master,导入了过多的commit,导致充满了很多无用的commit记录,如果有一天线上出现问题需要回滚代码,大量的commit记录会让你爆炸。

slice

我这里实际的演示,我在我的开发分支 feature1 开发了一个功能,有多条commit 记录,我想要合并成一条,然后提交MR到master(main)

Note: githubPull RequestgitlabMerge Request,只是叫法不一样。

这个时候就需要用到git rebase了。这个命令并不难,对于git命令的新手来说不熟悉的情况下可能不太敢使用,你可以在用之前先备份你的分支,避免操作失误又不会还原。

1. 合并最近的三条提交记录

执行:

git rebase -i HEAD~3

或者

git rebase -i commit_id

这里的commit_id是你要合并的第一条commit的前一条。可以通过 git log查看 commmit id。

会自动进入vi编辑模式。 slice

这里可以看到我们有三次提交记录:

pick 3ae8e31 fix 1
pick c2828fb fix 2
pick 6077ab1 fix 3

# Rebase c0933b9..6077ab1 onto c0933b9 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')

这里有一些Commands,每个有不同的作用,上面都有详细的解释。

我们暂时只需要关注两个,一个是 p(pick), 一个是 s(squash)

  • p, 保留该commit
  • s, 将该commit和前一个commit合并

把第一个commit使用 p,后面的所有commit都使用 s

2、把除第一条commit外都改成squash

pick 3ae8e31 fix 1
s c2828fb fix 2
s 6077ab1 fix 3

像以上这样,把除了第一条提交之外的所有提交的 pick 改成 s(squash)。 然后使用 :wq 退出编辑。

3、修改commit信息(可选)

如果你觉得第一条的commit不太好,想改成一个比较可读性的commit记录,可以直接修改。 slice

这里我把

fix 1

直接编辑 修改为

feature 1 development

然后 :wq 退出。

4、 git log查看commit记录。

这个时候会发现git log里面关于你的feature只有一条commit记录了,就是rebase成功了。

5、git push -f

最后一步就是执行 git push -f 强制push到远端。一定要加 -f 或者 –force。不然无法push成功。因为你当前的本地分支已经回到了之前的提交点。

这个时候刷新一下你的MR的页面。就会发现:

slice

你的commit已经合并了。这个时候你在Merge到master分支,就只有这一条commit记录了。是不是非常干净了。

注意:

  • 如果你的分支里面有来自其他分支的Merge Request,比如你的开发周期比较久,在你开发的几次提交中间,不停的git merge master,导致你的分支里面有了master最新的修改。也就是MR的记录,慎用rebase,这个时候我一般会用git reset。只有当你的分支里面全部是你的commit记录的时候,用git rebase

二、合并分支

rebase的第二大作用就是合并分支了。

rebase 通常用于重写提交历史。下面的使用场景在大多数 Git 工作流中是十分常见的:

  • 我们从 master 分支拉取了一条 feature1 分支在本地进行功能开发
  • 远程的 master 分支在之后又合并了一些新的提交
  • 我们想在 feature1 分支集成 master 的最新更改

下面用具体的例子来描述

1、我们先从 master 分支切出一个 feature1 分支,进行开发:

git:(master) git checkout -b feature1

git

2、这个时候你的同事可能开发了 feature2 并合并到了 master,此时master 已经领先于你的 feature1 分支了。

git

3、之后我们需要同步 master 的改动。

最常用的做法就是,在你的feature1分支执行:

git merge master

git

使用过git命令的人都了解这个命令。 合并之后查看一下log。执行:

git log

就会发现多了一些 merge 的commit信息。如果你的feature的开发周期很长,经常需要定期merge master,就会发现多了很多的 commit 信息。想要保持一份干净的 commit, 这个时候 git rebase 就派上用场了。

4、使用 git rebase master 代替 git merge master

在你自己的feature1分支上执行:

git rebase master

这里 git rebase 的原理是什么呢?

  1. git 会把 feature1 分支里面的每个 commit 取消掉;
  2. 把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
  3. 把 feature1 分支更新到最新的 master 分支;
  4. 把上面保存的 patch 文件应用到 feature1 分支上;

git

commit 记录我们可以看出来,feature1 分支是基于 feature2 合并后的 master,就像把一个并行的流程变成一个串行的流程。就没有了 mergecommit 记录,看起来会清晰多了。

Note:
在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。

git rebase --continue

在任何时候,我们都可以用 –abort 参数来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态。

git rebase —abort

总结:

  • 当我们在一个过时的分支上面开发时,执行 git rebase 来同步 master 分支的最新改动
  • 相比较merge来说会减少分支合并的记录

注意:

  • 不要在公共分支使用rebase,rebase会改变commit历史,其他依赖此分支的人可能会丢失记录。

总结

通过 rebase 策略执行 git pull Git 在最近的某个版本起,直接运行 git pull 会有如下提示消息:

warning: 不建议在没有为偏离分支指定合并策略时执行 pull 操作。 您可以在执行下一次 pull 操作之前执行下面一条命令来抑制本消息:

  git config pull.rebase false  # 合并(缺省策略)
  git config pull.rebase true   # 变基
  git config pull.ff only       # 仅快进

原来 git pull 时也可以通过 rebase 来进行合并,这是因为 git pull 实际上等于 git fetch + git merge ,我们可以修改git配置直接使用 git rebase 替换 git merge来合并 fetch 取得的变更,作用同样是避免额外的 merge 提交以保持线性的提交历史。

具体的使用方式有多种:

  • 每次执行 pull 命令时添加特定选项: git pull --rebase
  • 为当前仓库设定配置项: git config pull.rebase true,在 git config 后添加 –global 选项可以使该配置项对所有仓库生效。

结论

git-rebase 存在的价值是:对一个分支做「变基」操作。

  1. 当我们某一个分支有过多的 commit 记录需要整合,执行 rebase 来合并提交。
  2. 当我们在一个过时的分支上面开发的时候,执行 rebase 以此同步 master 分支最新变动;
  3. 假如我们要启动一个放置了很久的并行工作,现在有时间来继续这件事情,很显然这个分支已经落后了。这时候需要在最新的基准上面开始工作,所以 rebase 是最合适的选择。

​<全文完>