# 1. 分支变基(Git Rebase)

git rebase 能实现和 git merge 同样的功能,它可以以另一种方式来实现 “分支合并” 的效果。

# merge 和 rebase

假设我们有两个分支(master 和 feature)。feature 是基于 master 的 C1 节点建立的分支,然后开发人员分别在两个分支各自开发:

git-rebase-1

一个人在 C1 版本基础上开发出了 C2 版本;另一个人在 C1 基础上开发出了 C3 版本。

# merge 命令

现在我们想要把 feature 分支开发的内容合并到 master,使用 merge 命令:

$ git checkout master
$ git merge feature

复习

merge 命令的使用是 “站在 master 分支” 的角度上来看,将 feature 分支的内容 “纳入” 到 master 分支。master 分支会演进出一个新的版本。

git-rebase-2

从整体来看,merge 命令执行之后,会在之前的相关的 3 个版本节点之外生成一个新的节点 这个新节点将会是 master 线路上的最新的 “终点”

# rebase 命令

对于同样的初始情况,如果我们使用 rebase 命令会有什么不同。

git rebase 的正确使用方式和 git merge 有一点是完全相反的:

  • 使用 git merge 时,你是站在 A 分支,考虑把 B 分支 “合并进来”;
  • 使用 git rebase 时,你是站在 B 分支,考虑把 B 分支的 “基” 变成 A 的某个节点(通常是端点,即,最新的节点)

你要执行的命令如下(再次强调,你是站在 feature 分支上)

$ git checkout feature
$ git rebase master

在 GitKraken 中等价的操作如下:

git-rebase-3

执行 git rebase 的前后差异如下:

git-rebase-4

# 后续操作

虽然经过 git rebase ,feature 分支上已经含有了 master 分支和自己的所有的变更,但是通常这些代码最终是要合并到 master 分支上的,即,逻辑上 git rebase 并非一系列操作的 “终点” ,而是 “中点” 。

所以,最后我们还要回到 master 分支上,执行一次 git merge 操作,将 feature 分支合并进 master 分支。(再啰嗦一边,合并的时候,你是站在 A 分支,把 B 分支合进来、合进来、合进来。什么叫合进来?)

整个流程的总而言之一句话:feature rebase on master,master merge feature

# 2. 关于压缩提交(git rebase -i)

git rebase 名了除了用于 “另一种” 的合并之外,还有一种常见用途就是用来压缩提交记录。“学名” 叫交互式变基础(Interactive Rebase)。

效果看起来挺明显、挺直观的:

git-rebase-8


有这种使用场景也很常见:有时你以为你的有一次提交完成了所有工作,但是最后的结果却是你随后陆陆续续还要再补充几次提交才算是完成了原计划的一项任务。

这种情况下,你在 Git 的提交记录中将留下多个历史版本。而实际上,这多个版本并无存在的必要,逻辑上,只需要有一个就行了。

理想情况下,你可以用 git rebase -i 命令把多个 Commit 压缩成一个。

git rebase -i HEAD~<N>

例如,如果你想要压缩最后两个 Commit:

git rebase -i HEAD~3

注意

这里有 [HEAD, HEAD~3) 左开右闭的含义,因此,合并最后 2 个 Commit,出现的数字是 3 而不是 2

当你执行完这个命令后,合并工作并没完,你看到的是一个交互式(interactive )界面,例如:

pick 16a9a47 update3 
pick a7186d8 update2
pick 7b16b28 update1

 Rebase a9269a3..7b16b28 onto a9269a3 (3 commands)

 Commands:
 p, pick = use commit
 r, reword = use commit, but edit the commit message
 e, edit = use commit, but stop for amending
 s, squash = use commit, but meld into previous commit
 f, fixup = like "squash", but discard this commit's log message
 x, exec = run command (the rest of the line) using shell
 d, drop = remove commit

...

在这里,Git 需要我们手动对这 3 个 Commit 进行设置。我们需要关注的状态是 SquashReword 。我们要将这 3 个 Commit 调整成如下状态:

squash 16a9a47 update3 
squash a7186d8 update2
reword 7b16b28 update1

这样,最近的 2 次 Commit(HEAD、HEAD~1)将合并到 HEAD~2 中,并且它们两个的 message 将会被合并到 HEAD~2 的 message 中。

在 GitKraken 中,它们是这个样子:

git-rebase-9