[Git] Rebase - 合併的另種選擇

  • 7851
  • 0
  • Git
  • 2016-04-30

在 Merge 前思考一下,此情境是否使用 Rebase 比較優雅呢?

前言

在使用 Git 做為開發版本控管時,通常會建立 master 作為主要分支,然後建立 develop 分支做為開發使用,等待 develop 分支完成所需之各個開發任務後,再將 develop 上所有異動併回 master 主要分支中,並可以此節點作為產品 release 時機點;這是常見的基本Git板控流程,因此合併(Merge)功能應該是比較可以理解的範疇。

接著請大家試想一個情境,開發團隊預計完成兩項功能的開發,當工程師用 develop 分支開發完功能一後,就在開發功能二的途中發現共用模組有很嚴重的 Bug 存在,會影響接續的開發(甚至線上系統也會有問題);因此會選擇先 check out 到 master 分支進行Bug修復,等待修復完成後立刻 commit 建立節點,而開發團隊就可以使用master最新節點來發佈修正版本的程式。

接著為了順利完成 develop 分支接續的開發工作,因此需把 master 剛剛修復共用模組的變更合併到 develop 分支中,好讓 develop 分支中的錯誤可以被排除,方便繼續開發功能二。此時有兩種選擇,第一種就是使用 merge 做反向合併(develop merge master),為何說反向是因為通常都是主幹分支來合併其他分支,而目前情況是需要讓分支來合併主幹;接著在完成功能二後,我們又需要執行正向合併(master merge develop),讓 develop 的變更回歸到 master 主要分支中,而若以此情況繼續演進下去,不難想像最後 master 與 develop 分支會有互相交織的複雜網狀演進圖存在(如下圖所示)。

因此我們可以考慮選擇另一種方式進行,就是使用 rebase 來將 develop 分支重新接枝到指定的 master 分支節點,讓節點演進線圖比較單純好懂,但此舉會造成 develop 上舊 commit 節點被異動(因為長出分支的節點位置已經變動),如果目前分支是有與其他人共用的話,就不適合使用 rebase 指令。執行 rebase 後節點演進圖如下所示,很單純的移動 develop 分支節點至 Bug 修復完成的 master 節點上,從演進圖來看是相當好理解。

 

環境

  • git v2.8.1.windows.1
  • SourceTree  v1.3.8.0

 

實際演練

首先會建立一個如下圖的版本演進情境,分別在 master 及 develop 分支上都有資料的異動紀錄,而我們的目標就是要將 master 分支上修復錯誤的代碼併入 develop 分支中,使得兩者基礎一致讓開發不會產生原有錯誤。筆者將在後續以此基礎執行 rebase 及 merge 來比較差異。

首先開啟 SourceTree ,點選 Clone/New 來建立一個新 Local Repository。

輸入資料夾位置就可以建立一個新 Repository

以上所執行的對應指令如下

cd d:\GitTester\Project3
git init

 

直接在資料夾上建立 qoo.txt ,內容如下來表示既有的程式代碼。

commit 剛建立的檔案作為初始資料

以上所執行的指令為

git add -A .
git commit -m "first commit"

目前的演進圖就只有一個節點

 

接著建立一個新的分支 develop

以上所執行的指令為建立一個新的 branch 並切換到該 branch。

git checkout -b "develop"

或者

git branch "develop"
git checkout "develop"

演進圖就會多了 develop 分支,可以讓我們隨意切換(Checkout)

 

接著來模擬在 develop 分支中完成功能一代碼。首先 checkout develop 分支,然後修改 qoo.txt 加上以下文字。

commit 此次異動

以上所執行的指令為

git add -A .
git commit -m "完成功能一"

演進圖確實出現一筆 develop 分支的 commit 節點

 

再來模擬發現共用模組 Bug,需緊急在 master 分支中完成修復代碼。首先 checkout master 分支。

git checkout master

然後修改 qoo.txt 加上以下文字。

commit 此次異動

以上所執行的指令為

git add -A .
git commit -m "緊急修復錯誤"

演進圖出現一筆 master 分支的 commit 節點

 

Rebase

由於我們希望將 develop 分支 rebase 到 master 分支中最新節點,也就是變更 develop 分支點至 master 最新節點,因此我們要切換(checkout)為 develop 分支。

以上所執行的對應指令如下

git checkout develop

 

右鍵點選 master 最新節點,選擇 rebase

以上所執行的對應指令如下

git rebase master

 

衝突發生

修正衝突

改完

加入 index 中 (add in staged files)

以上所執行的對應指令如下

git add -A .

 

點選 Action -> Continue Rebase 來繼續執行 Rebase

以上所執行的對應指令如下

git rebase --continue

 

下圖是執行Rebase前後的演進圖,可以發現差如下:

1. 執行 rebase 後,develop 分支的【完成功能一】節點已經接上 master 分支的【緊急修復錯誤】節點(表示包含修復代碼),從圖上可以很清楚的了解 develop 分支是base在【緊急修復錯誤】後的延伸。

2. 執行 rebase 後,develop 分支的【完成功能一】節點其實已經不是原本的那個節點了(e994361 to 4af7491),因為會一個一個將 develop 分支的 commit 重新 apply 到要被 rebase 的節點上。因此如果目前分支是有與其他人共用的話,就不適合使用 rebase 指令。

 

Reset

嘗試了以 rebase 方式合併分支後,筆者為了比較於此情境下使用 merge 來合併分支有何差異,因此透過 git 的 reset 指令來重置版本至 rebase 前。使用的方式很簡單,首先就開啟 Terminal 來用指令處理一下。

開啟後輸入 git reflog HEAD 來取出HEAD相關的log資料

可以發現紅色區塊就是剛剛執行的 rebase 作業,因此我們需要將版本 reset 到 HEAD@{3} 才是我們想要的

實際執行 reset 的方法也很簡單,直接輸入 git reset --hard HEAD@{3} 後執行,其中 –-hard 參數表示不留 staged files 也不留 working tree (完全刪除任何修改記錄),最後畫面上就會顯示目前 HEAD 已經移動到我們指定的位置了,

不相信在看一下演進圖,真的就回復到執行 rebase 之前的狀態

 

Merge

由於我們希望以 develop 分支來合併 master 分支,因此我們要切換(checkout)為 develop 分支。

以上所執行的對應指令如下

git checkout develop

 

右鍵點選 master 最新節點,選擇 merge 選項

警告訊息表示再次確認,其中下方的選項表示是否強制建立新 commit 節點。這邊順便說明一下,在使用 merge 時預設會使用 fast-forward 方式進行,也就是如果有 fast-forward 的可能性(異動只存在於其中一個分支中、不會有衝突產生),在 merge 分支時是不會建立出新節點,直接將分支快轉至合併點,因此也就不會有合併 commit 紀錄存在,所以如果要強制建立出 commit 紀錄時就要勾選該選項;另外如果兩分支都有異動,在合併時就屬於 3-way merge ,此種方式往往就會有很大的機會遇到衝突,並且也一定會建立出新的合併節點。

以上所執行的對應指令如下

git merge master

 

衝突出現了

調整一下代碼來解決衝突

改變後

將改變加入index (in staged files)

以上所執行的對應指令如下

git add -A .

 

提交剛剛衝突的修正

以上所執行的對應指令如下

git commit

看一下演進圖,develop 分支就確實合併了 master 分支,建立了一個屬於 develop 分支的 merge commit 節點,其中表示此節點包含master分支之【緊急修復錯誤】修復代碼。

 

Rebase vs Merge

結論兩者都可以達到相同目的,些微差異如下:

1. rebase 會異動到 develop 接枝後所有 commit 節點,而 merge 不會異動目前存在之節點。

2. 以此情境下 rebase 演進圖比較直覺單純,而 merge 會新增一個節點(如果不是fast-forward)讓線圖不易理解​

3. rebase 會逐一處理每個 commit 節點之衝突,而 merge 會統一處理所有衝突。

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !