Rebase的作用与之前提到的merge相似。它的作用是将一个分支的commits加到另一个分支上去。
比如我们有:
如果使用merge,结果如下图所示:
而如果使用rebase:
git checkout experiment
git rebase master
则结果如下图所示:
C4的改动内容会在C3上进行重做,产生C4’,而C4会被“删除”(C4与C4’的hash值并不同)
这时候我们可以使用
git checkout master
git merge experiment
让master合并experiment,但此时从图中我们可以看出experiment已经“包含”master,所以实际上并不会合并发生,而是进行“fast-forward”,也就是让master直接指向C4’就行了。结果如下图:
我们再看一个复杂的例子:
假设我们此时想把client分支的C8、C9两个commits加到master分支中,我们可以:
git rebase --onto master server client
它的意思是,把client分支中有的,server分支中没有的(即C8、C9),加到master上去。结果如下图:
然后再按上文进行一个“fast-forward”,让master指向C9’:
git checkout master
git merge client
这里如果我们使用
git checkout master
git merge client
会将C3的内容也加到master上去,而且结构图会显得比较复杂。
然后假如我们又想把server的内容也整合到master,可以使用:
git rebase master server
这里相当于
git checkout server
git rebase master
就可以将server也加上去了
然后进行“fast-forward”
git checkout master
git merge server
最后进行清理,删除这两个已经无用的分支:
git branch -d client
git branch -d server
你也可以指定<from_branch>
而不使用默认参数HEAD(即当前所在的位置)
git rebase <to_branch> <from_branch>
如果要指定更详细的范围,比如下图中,想把C8、C9 rebase到master上:
可以使用git rebase --onto master server client
它的意思是将server..client
rebase到 master
(注:server..client在Git中的意思是client中含有的而server中不含有的那些commits,即C8、C9)
The Perils of Rebasing
使用rebase需要遵守一条准则:
不要rebase在你的本地仓库以外也存在着的commits。
换句话说,如果你将要rebase的commits曾经被你push到某个其他人也可能会使用的服务器上时,就不该使用rebase。
比如下图,假设在某服务器teamone上有人push了C4、C5、C6,你将它们pull到本地与本地的master(C2、C3,此时还没push到服务器上)合并成了C7后,如图:
这时候,该人删除了C6,改用rebase将C4 rebase到C5上,并且push到team1服务器上。在他的视角看来,这样做更干净了:
然而,当你pull服务器上的内容后,本地仓库变成了这样:
因为C4、C6早已存在你的本地仓库了。这时候结构变得相当混乱,很容易让人误解。虽然并不会让本地数据产生错乱。
因此,最好遵守该准则:
不要rebase在你的本地仓库以外也存在着的commits。
如果已经发生了这种情况,那么可以用
git fetch
git rebase teamone/master
或
git pull --rebase
git会自动分析,并帮你整理成这样:
最后总结一下,git merge和 git rebase各有各的优势,你应该根据你想要让commit history看起来如何来选择使用哪个,并且如果用rebase的话不要忘了那条准则。