Git 201 - Intro Rebase

Rebase 是 Git 课程中最让人费解的一个部分了,但理解过后,却也发现其强大之处。可以最大限度的用来保证项目提交历史的整洁,但其也有负面性,接下来我们用示例来看。

分支合并

Rebase 和 Merge 一样主要用于分支的合并,但二者方式却不同,Merge 判断如果是 FastForward,则直接移动头指针,否则通过找到两个分支的共同祖先,然后进行第三方合并,产生新的Commit。

Rebase 不同,它是将你在一个分支里所做的所有 commit,到另一个分支再重新按顺序一个个重做一遍。

下面用个示例来看(来自gitready.com),首先创建一个新的分支,然后在该分支里进行一些提交。

$ git checkout -b newfeature `
Switched to a new branch "newfeature"

[.. changed some files, made a few commits ..]

$ git checkout master
Switched to branch "master"

[.. changed one file, committed ..]`

这时,历史记录看起来如下(gitx)

现在如果想把新的分支合并回master分支,可以使用 git merge newfeatre,这样的话,合并过的分支提交记录则看起来如下:

但如果是使用rebase而非merge,git rebase newfeature,那历史记录看起来会是如下:

可以看出,整个 master 分支主线很突出,而没有其它分支的干扰。newfeature 分支的提交又被一个个的重新应用到了 master 分支。因为是重新应用,所以 SHA-1 值也是全新的,这一些值得注意。

之前 fast-forward 中有提到过,为了保留版本开发过程中的分支信息,要使用 --no-ff 参数来强制保留开发中的分支信息。但这里却要想办法移除开发过程中的版本信息。

可能仅在示例层面上无法理解这样操作的用意,但在大型的项目开发流程中便能体会这种重要性。如果是在开发一个完整的 feature,在最后合并时很显然你需要保留这个 feature 开发过程所使用的分支等信息。但若是为一个 feature 做一些小的尝试性的工作,而该 branch 在完成之后立即会被删除,这时,可以选择 rebase 来将分支历史变得整洁。

修改提交历史记录

除了分支合并,Rebase 还用于修改已提交的历史记录,按要求进行重新排序(reordering)、将一个 commit 分解或者将多个小的提交进行合并重组(squashing)。

公司曾开会讨论过这方面的内容,我们应该频繁提交代码,这对于开发者个人来说是有用的,但对于将来做 Code Review 来说,却会带来麻烦。但若换成,做完一个整 Feature 再进行 Checkin,又失去了版本控制的意义。Git 的中的 Rebase 就是致力于解决这类问题。

试想,所有的开发都在本地,在未Push进中心控制器之前,你可以任由自己来做提交和修改,在 push 前,对所有的 commit 记录再进行整理(合并、修发),最后推送出去的 commit 非常干净,整洁。两全其美的方案。

但一定要记得,千万不要对 push 到中心服务器上的历史记录进行修改。引用 Git Pro 上的一句话:如果你这样做,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

这是因为,如果别人在一个已被提交的基础上工作,而你用 rebase 重写了这些提交对象并推送到中心服务器,那个其于之前代码的开发者就不得不重新合并他的工作,当他推送后你再获取代码时,整个提交历史会让人崩溃的。