【轉】git rebase 用法

3.6 Git Branching - Rebasing

Rebasing

把一個分支整合到另一個分支的辦法有兩種:merge(合併) 和 rebase(衍合)。在本章我們會學習什麼是衍合,如何使用衍合,爲什麼衍合操作如此富有魅力,以及我們應該在什麼情況下使用衍合。

Rebase基礎

請回顧之前有關合並的一節(見圖 3-27),你會看到開發進程分叉到兩個不同分支,又各自提交了更新。

之前介紹過,最容易的整合分支的方法是 merge 命令,它會把兩個分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)進行三方合併。如圖 3-28 所示:

其實,還有另外一個選擇:你可以把在 C3 裏產生的變化補丁重新在 C4 的基礎上打一遍。在 Git 裏,這種操作叫做衍合(rebase)。有了 rebase 命令,就可以把在一個分支裏提交的改變在另一個分支裏重放一遍。

在這個例子裏,可以運行下面的命令:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理是回到兩個分支(你所在的分支和你想要衍合進去的分支)的共同祖先,提取你所在分支每次提交時產生的差異(diff),把這些差異分別保存到臨時文件裏,然後從當前分支轉換到你需要衍合入的分支,依序施用每一個差異補丁文件。圖 3-29 演示了這一過程:

現在,你可以回到 master 分支然後進行一次快進合併(見圖 3-30):

現在,合併後的 C3(即現在的 C3')所指的快照,同三方合併例子中的 C5 所指的快照內容一模一樣了。最後整合得到的結果沒有任何區別,但衍合能產生一個更爲整潔的提交歷史。如果視察一個衍合過的分支的歷史記錄,看起來更清楚: 彷彿所有修改都是先後進行的,儘管實際上它們原來是同時發生的。

你可以經常使用衍合,確保在遠程分支裏的提交歷史更清晰。比方說,某些項目自己不是維護者,但想幫點忙,就應該儘可能使用衍合:先在一個分支裏進行開發,當準備向主項目提交補丁的時候,再把它衍合到origin/master 裏面。這樣,維護者就不需要做任何整合工作,只需根據你提供的倉庫地址作一次快進,或者採納你提交的補丁。

請注意,合併結果中最後一次提交所指向的快照,無論是通過一次衍合還是一次三方合併,都是同樣的快照內容,只是提交的歷史不同罷了。衍合按照每行改變發生的次序重演發生的改變,而合併是把最終結果合在一起。

更多有趣的衍合

你還可以在衍合分支以外的地方衍合。以圖 3-31 的歷史爲例。你創建了一個特性分支 server 來給服務器端代碼添加一些功能,然後提交 C3 和 C4。然後從 C3 的地方再增加一個 client 分支來對客戶端代碼進行一些修改,提交 C8 和 C9。最後,又回到 server 分支提交了 C10。

假設在接下來的一次軟件發佈中,你決定把客戶端的修改先合併到主線中,而暫緩併入服務端軟件的修改(因爲還需要進一步測試)。你可以僅提取對客戶端的改變(C8 和 C9),然後通過使用 git rebase 的 --onto 選項來把它們在 master 分支上重演:

$ git rebase --onto master server client

這基本上等於在說“檢出 client 分支,找出 client 分支和 server 分支的共同祖先之後的變化,然後把它們在master 上重演一遍”。是不是有點複雜?不過它的結果,如圖 3-32 所示,非常酷:

現在可以快進 master 分支了(見圖 3-33):

$ git checkout master
$ git merge client

現在你決定把 server 分支的變化也包含進來。可以直接把 server 分支衍合到 master 而不用手工轉到 server分支再衍合。git rebase [主分支] [特性分支] 命令會先檢出特性分支 server,然後在主分支 master 上重演:

$ git rebase master server

於是 server 的進度應用到 master 的基礎上,如圖 3-34:

然後快進主分支 master

$ git checkout master
$ git merge server

現在 client 和 server 分支的變化都被整合了,不妨刪掉它們,把你的提交歷史變成圖 3-35 的樣子:

$ git branch -d client
$ git branch -d server

衍合的風險

呃,奇妙的衍合也不是完美無缺的,一句話可以總結這點:

永遠不要衍合那些已經推送到公共倉庫的更新。

如果你遵循這條金科玉律,就不會出差錯。否則,人民羣衆會仇恨你,你的朋友和家人也會嘲笑你,唾棄你。

在衍合的時候,實際上拋棄了一些現存的 commit 而創造了一些類似但不同的新 commit。如果你把commit 推送到某處然後其他人下載並在其基礎上工作,然後你用 git rebase 重寫了這些commit 再推送一次,你的合作者就不得不重新合併他們的工作,這樣當你再次從他們那裏獲取內容的時候事情就會變得一團糟。

下面我們用一個實際例子來說明爲什麼公開的衍合會帶來問題。假設你從一箇中央服務器克隆然後在它的基礎上搞了一些開發,提交歷史類似圖 3-36:

現在,其他人進行了一些包含一次合併的工作(得到結果 C6),然後把它推送到了中央服務器。你獲取了這些數據並把它們合併到你本地的開發進程裏,讓你的歷史變成類似圖 3-37 這樣:

接下來,那個推送 C6 上來的人決定用衍合取代那次合併;他們用 git push --force 覆蓋了服務器上的歷史,得到 C4'。然後你再從服務器上獲取更新:

這時候,你需要再次合併這些內容,儘管之前已經做過一次了。衍合會改變這些 commit 的 SHA-1 校驗值,這樣 Git 會把它們當作新的 commit,然而這時候在你的提交歷史早就有了 C4 的內容(見圖 3-39):

你遲早都是要併入其他協作者提交的內容的,這樣才能 保持同步。當你做完這些,你的提交歷史裏會同時包含 C4 和 C4',兩者有着不同的 SHA-1 校驗值,但卻擁有一樣的作者日期與提交說明,令人費解!更糟糕的是,當你把這樣的歷史推送到服務器,會再次把這些衍合的提交引入到中央服務器,進一步迷惑 其他人。

如果把衍合當成一種在推送之前清理提交歷史的手段,而且僅僅衍合那些永遠不會公開的 commit,那就不會有任何問題。如果衍合那些已經公開的 commit,而與此同時其他人已經用這些 commit 進行了後續的開發工作,那你有得麻煩了。






轉自:http://blog.csdn.net/trochiluses/article/details/14451777
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章