一、簡介
? ? ? ? ? 在Git中,有兩種方法將兩個不同的branch合并。一種是通過git merge,一種是通過git rebase。然而,大部分人都習慣于使用git merge,而忽略git rebase。本文將重點介紹git rebase的原理、使用方式及應用范圍以及與git merge的區(qū)別。
二、git merge
當我們在開發(fā)一些新功能的時候,往往需要建立新的branch。
在上圖中,每一個綠框均代表一個commit。除了c0,每一個commit都有一條有向邊指向它在當前branch當中的上一個commit。項目在c2之后就開了另外一個branch,名為experiment。在此之后,master下的修改被放到c4 commit中,experiment下的修改被放到c3 commit中。
如果我們使用merge合并兩個分支,比如講experiment上的提交(c3)合并到master上,則可以使用如下命令:
Shell代碼?
? 1. $ git checkout master?
? 2. $ git merge experiment?
merge后得到的commit log如下圖所示
我們看到,merge所做的事情實際上是:
? 1. 首先找到master和experiment中最新的commit的最近公共祖先,在這里就是c4和c3的最近公共祖先c2。
? 2. 將experiment分支上在c2以后的所有commit合并成一個commit,并與master合并
? 3. 如有合并沖突(兩個分支修改了同一個文件),首先人工去除重復。
? 4. 在master上產(chǎn)生合并后的新commit。
三、git rebase
rebase所做的事情也是合并兩個分支,但是它的方式略有不同。基于上例描述,rebase的工作流程是:
? 1. 首先找到master和experiment中最新的commit的最近公共祖先,在這里就是c4和c3的最近公共祖先c2。
? 2. 將experiment分支上在c2以后的所有commit*全部移動到*master分支的最新commit之后,在這里就是把c3移動到c4以后。
由于git的每一個commit都只存儲相對上一個commit的變化(或者說是差值,delta)。我們通過移動c3到master,代表著在master上進行c3相應的修改。為了達成這一點,只需在experiment分支上rebase master
Shell代碼?
? 1. $ git checkout experiment?
? 2. $ git rebase master?
需要注意的是,rebase并不是直接將c3移動到master上,而是創(chuàng)建一個副本。我們可以通過實際操作發(fā)現(xiàn)這一點。在rebase前后,c3的hash code是不一樣的。
rebase前的commit log是
Log代碼?
? 1. * 1b4c6d6 (master) <- c4?
? 2. | * 66c417b (experiment) <- c3?
? 3. |/? ?
? 4. *? 972628d?
rebase后的commit log是
Log代碼?
? 1. * d9eeb1a - (experiment) <- c3'?
? 2. * 1b4c6d6 - (master) <- c4?
? 3. * 972628d?
可以發(fā)現(xiàn)c3的hash code從66c417b變到了d9eeb1a。
在這之后,我們只需要在master上進行一次前向合并(fast-forward merge)
Shell代碼?
? 1. $ git checkout master?
? 2. $ git merge experiment?
rebase之后的commit log呈線性,更加清晰。此時如果experiment分支不再被需要,我們可以刪除它。
Shell代碼?
? 1. $ git branch -d experiment?
四、何時使用
我們一般只在本地開發(fā)的時候rebase一個自己寫出來的branch。
謹記,千萬不要rebase一個已經(jīng)發(fā)布到遠程git服務器的分支。例如,你如果將分支experiment發(fā)布到了GitHub,那么你就不應該將它rebase到master上。因為如果你將它rebase到master上,將對其他人造成麻煩。
總結(jié)
git rebase幫助我們避免merge帶來的復雜commit log,允許以線性commit的形式進行分支開發(fā)。