神奇且无害的REBASE

2014年底翻译了这篇文章,当时发表在GotGit 群组,搬过来留存一下

作者:Jeff Kreeftmeijer 发表于 2010-10-11 原文链接

大约一个月前, 以Git为主题我写了不少文章,始于@nvie 出色的git开发流程教你写出更好的提交注释,终结于强大的reflog和惊人强大的bisect 。你猜怎么着?我竟然忘记了介绍神奇的rebase

人们通常认为rebase是一种压合提交工具,但这并非它的看家本领。顾名思义,它的本职工作是变基(改变我们所做变更的基础)。

哦,忘掉那篇吓唬人的文章,只要清楚自己在做什么,根本不会产生什么恶果。

假设你正工作在一个名叫feature/login的新功能分支上,某个家伙实现了某样东西,并把他的代码push到了develop分支上。你需要那个东西,该怎么做?

可以把develop分支合并到你正在工作的分支,这会产生一些....乱七八糟的合并提交,不好。

也可以把我们需要的那个提交cherry-pick过来。不过--经验表明--虽然不是什么大问题,从一堆提交里找出需要拣选的提交,终究是个麻烦事。

Git的rebase指令允许我们先回退自己的改动(类似“倒带”),拉回另一个分支的所有变动之后(变基),在新的基础(HEAD)上“重放”我们的变更:

$ git rebase develop

First, rewinding head to replay your work on top of it...
Fast-forwarded feature/login to develop.

仿佛在拉回那些变更之前,我们什么也没做(我们所有的改动仿佛发生那些变更之后)。这非常好,是不是?我们还能在pull的同时变基(使用--rebase选项),这样的话我们甚至都不需要离开自己的工作分支。

更小块的冲突

rebase让我们拥有干净的提交历史,即便在rebase的过程中发生冲突,它也是我们解决冲突的坚强后盾:

$ git rebase develop
First, rewinding head to replay your work on top of it...
Applying: feature/login
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging config/environment.rb
CONFLICT (content): Merge conflict in config/environment.rb
Failed to merge in the changes.
Patch failed at 0001 feature/login

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".

由于rebase顺序重放每一个提交,冲突会以更小的块(更多的次数)出现,这有助于我们迅速理解冲突原因,更快地解决冲突。在解决冲突之后,只需git add冲突文件,然后输入下面的命令继续rebase过程:

$ git rebase --continue

rebase 还是 merge

当我们工作于一个功能分支,发现需要 develop 分支上的所有变更时,我建议使用rebase。当我们的功能分支已经开发完成,应该使用 merge 把功能分支合并回 develop分支。这样我们能够记录下我们在何时把分支合并到 develop 主干,就是利用那个...我们前面说过的“乱七八糟”的提交。在这个时候,它真的不是乱七八糟的提交,确实不是。

在合并功能分支之前,我希望你使用rebase。这样我们方能保证自己的分支在合到并主干时仍然拥有干净的提交历史。

你用过rebase吗?你在何时使用 rebase 不用 merge?还是只用 rebase?你还害怕rebase吗?欢迎撰写评论留下你的想法,让我知道。

PS: 这篇文章虽然写于2010年,但写的清晰明快。我今天(2014年)把它翻译过来,希望能帮助到更多的人。rebase是一个很好的工具,只要你愿意了解它。