如何使用Git Rebase

我們在日常開發中使用 Git 做分支合併的時候有兩種方式:merge 和 rebase。merge 是最常用的,rebase 使用的沒有 merge 這麼多,那麼 rebase 和 merge 有什麼區別呢?什麼時候使用 rebase?使用 rebase 的時候有什麼注意事項呢?接下來我來介紹下這三個問題。

基礎

首先我們先了解下 merge 和 rebase 的運行機制有什麼不同。假設我們有兩個分支(master 和 feature)。feature 是基於 master 的 C1 節點建立的分支,然後開發人員分別在兩個分支各自開發:

圖片描述

merge

現在我們想要把 feature 分支開發的內容合併到 master,使用 merge 命令:

$ git checkout master
$ git merge feature

圖片描述

Git 會把 C2 和 C3 的內容合併一下產生一個新的修改 C4,把 C4 提交到 master 分支。那麼 master 就有了所有最新的代碼(紅色是 master 分支線,藍色是 feature 分支線)。

rebase

我們回到還是未合併之前的狀態,看看使用 rebase 合併會有什麼效果。使用 rebase 有兩種用法,先看第一種:

$ git checkout master
$ git rebase feature

圖片描述

Git 把 C2 備份成 C2',刪除 C2,然後把 C2' 追加到 C3 後面,把 master 指向 C2'。看圖上紅線,成爲了新的 master,這條線上同樣有所有的最新代碼,起到的作用也是合併了兩個分支。那麼和 merge 有什麼區別呢?就是歷史時間線的區別。merge 保留了你所有的操作記錄,而 rebase 把提交的修改節點變成了線性的時間線。如果分支 merge 很多的話,時間線會錯綜複雜,這個時候 rebase 的好處就出現了,對人肉追溯比較友好。

我們再看下合併兩個分支 rebase 的第二種用法:

$ git checkout feature
$ git rebase master
$ git checkout master
$ git merge feature

圖片描述

前兩條命令我們先在 feature 上 rebase on master,產生的效果和第一種方法類似,只是把 feature 分支的改動追加到了 master 分支後面(紅色是 master 分支線,藍色是 feature 分支線)。

圖片描述

後兩條命令是把 feature 分支 merge 到 master 分支。由於 Git 發現不需要合併代碼,只需要移動頭指針就可以了,所以快速移動(fast forward)頭指針到最前面,master 分支這時就有所有最新代碼,同樣起到了合併分支的作用。

那麼第一種和第二種有什麼區別呢?直觀的看就是時間線上 C2,C3 順序的區別,對於我們合併分支最終的意圖是沒有什麼區別的。

注意點

如果你是一人開發,且只有本地倉庫,那麼上面 rebase 用哪種問題都不會太大。但是如果你是多人協作且有遠程倉庫,那麼區別就巨大了。因爲你合併完分支之後還需要 push 到遠程倉庫供別人使用,當你使用第一種方法後,C2 被放到了 C3 後面,你本地的 master 時間線變成了 C1 - C3 - C2',而遠程倉庫 master 的時間線是 C1 - C2。雖然 C2 和 C2' 是一樣的,但是兩者時間線歷史完全不一樣了,Git 認爲你這是兩個不一樣的分支,不允許你 push。這個時候只能用 $ git push --force 強迫提交,用本地的 master 覆蓋遠程倉庫的 master。但是 master 分支別人還在用啊,他們也需要同步 master 分支。但是別人本地的 master 分支並不知道你 rebase 過了遠程倉庫的 master,所以當他們 pull master 的時候,變成了如下時間線:

圖片描述

其他人本地的 master 時間線上會出現兩個 C2(C2 和 C2'),然後如果這人把上圖的 master 再 push 到遠程倉庫,會帶上上圖所有的歷史,多來幾次以後整個倉庫就沒有辦法看了。而第二種 rebase 的方法是把新的 commit 不斷的追加到 master 後面,只要所有人在往 master 上合併代碼的時候都遵循第二種方法,那麼就不會產生極其混亂的時間線。

什麼時候使用

按我們上面看到的,在現實的開發過程中,嚴格禁止在公共分支上 rebase on 其他分支(譬如不允許在 master 分支上直接運行 git rebase branchname)。使用 merge 是最保險的合併分支方式,如果你對時間線清晰度要求不是那麼高的話。但是如果你對時間線的清晰程度有比較高的要求,那麼在合併分支的時候按第二種方法 rebase 就能幫助形成清晰的線性時間線,但是 rebase 的壞處是丟失了一部分提交操作歷史。

同步分支

除了合併不同分支這種情況,還有一個十分常見的情況,多人在同一個分支上開發,如何保證同一條分支具有清晰的時間線。我們假設有三個人在開發 feature 分支,通常開發習慣是:

  1. checkout feature 分支到本地。
  2. 開發,並把開發的內容提交到本地 feature。
  3. 使用 pull 把遠程倉庫中的 feature 和本地 feature 同步(pull 的默認方式是把遠程的代碼和本地代碼做 merge 操作)。
  4. push 同步完的 feature 分支到遠程倉庫。

按上述步驟,我們看下時間線示意圖:
先是第1,第2步

圖片描述

用戶1執行3,4步

圖片描述

用戶2執行3,4步

圖片描述

用戶3執行3,4步

圖片描述

因爲 pull 默認是通過 merge 遠程倉庫和本地倉庫實現同步的,所以每次 pull 都會多出一個提交記錄。但是我們可以通過指定參數來指定 pull 的時候使用 rebase 策略:$ git pull --rebase。讓我們看下每次執行第3步的時候使用 pull rebase 那麼會是什麼樣子。

用戶1執行3,4步

圖片描述

用戶2執行3,4步

圖片描述

用戶3執行3,4步

圖片描述

通過圖示我們看到,如果使用 pull 的 rebase 模式,那麼多人協作同一個分支也可以做到時間線清晰。最後再通過之前提到的 rebase 方式把 feature 合併到 master 分支上。那麼整個開發過程時間線就呈現出清晰的時間線。

總結

  1. 和遠程倉庫同步當前分支的時候使用 pull --rebase 的方式。
  2. 合併分支的使用 feature rebase on master,master merge feature 的方式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章