再談上線合併的分支策略一文提到「合併前先 Rebase 能做出乾淨漂亮的線形」(如下圖之上下對照),但比較費事,有時需重複排除修改衝突,這篇文章就來實證。

照慣例,用一段 DOS 批次檔備妥實驗環境:

rem 請先建好實驗資料夾,並修改以下變數
set lab_folder=D:\GitTest
rem 切換到實驗資料夾,清空前次 Git 測試結果
cd /d %lab_folder% 
if exist %lab_folder%\upstream (rmdir /s /q %lab_folder%\upstream)
if exist %lab_folder%\jeffrey (rmdir /s /q %lab_folder%\jeffrey)
if exist %lab_folder%\darkthread (rmdir /s /q %lab_folder%\darkthread)
rem 建立 upstream Git Repository
mkdir %lab_folder%\upstream && cd %lab_folder%\upstream
git init --bare
rem 模擬 jeffrey 及 darkthread clone 建立本機 Repository 
cd %lab_folder%
git clone ./upstream ./jeffrey
git clone ./upstream ./darkthread
rem jeffrey,建立 master 並 push
cd %lab_folder%\jeffrey
git config user.name "Jeffrey"
git config user.email "jeffrey@mail.net"
echo  SharedFile > shared.txt
git add . && git commit -m "初始化"
git push
rem darkthread,pull 取得 master
cd %lab_folder%\darkthread
git config user.name "darkthread"
git config user.email "darkthread@mail.net"
git pull
rem jeffrey,修改前先建立開發分支,進行修改
cd %lab_folder%\jeffrey
git checkout -b feature/reqA
echo ForReqA1 >> shared.txt
touch reqA-1.txt
git add . && git commit -m "ReqA Commit 1" && git push
ping localhost -n 62 > NUL
rem darkthread,更新shared.txt提交至master
cd %lab_folder%\darkthread
echo TopLine >> shared.txt
git add . && git commit -m "Modify shared.txt" && git push
ping localhost -n 62 > NUL
rem jeffrey,進行修改
cd %lab_folder%\jeffrey
echo ForReqA2 >> shared.txt
touch reqA-2.txt
git add . && git commit -m "ReqA Commit 2" && git push
ping localhost -n 62 > NUL
echo ForReqA3 >> shared.txt
touch reqA-3.txt
git add . && git commit -m "ReqA Commit 3" && git push
ping localhost -n 62 > NUL
rem jeffrey,更新 master,準備合併feature/reqA
cd %lab_folder%\jeffrey
git checkout master
git pull --rebase
git checkout feature/reqA

如下圖所示,Jeffrey 從 master 拉出 feature/reqA 分支,陸續提交 Commit 1, 2, 3,而在 Commit 1 跟 2 中間,darkthread 在 master 加入了一個 Commit。這 4 個 Commit 都有修改到 shared.txt 檔案,feature/reqA 要併回 master,不管直接合併還是先 Rebase 再合併都必須解決衝突,但次數不同。

先看直接合併,feature/reqA 合併進 master 我們只需處理 ReqA Commit 3 最終版本 shared.txt 跟 darkthread 提交版本的衝突:

但如果 feature/reqA 先對 master Rebase,則需要分別處理 ReqA Commit 1、ReqA Commit 2、ReqA Commit 3 版本與 darkthread 提交 shared.txt 版本的衝突(理由是 Rebase 需重新產生 Commit),有些衝突可自動解決,若不幸無法自動處理就得手工處理三次:(以下範例我刻意做成無法自動解決衝突的情境,若在 ReqA Commit 1 同時保留 TopLine 及 ForReqA1,後面兩個 Commit 可自動解決衝突)

由以上案例可證明,先 Rebase 再合併比直接合併可能要處理更多次更新衝突。而這也就是前篇文章提到開發分支經常對 master 做 Rebase 可降低合併前 Rebase 的難度。在這個案例中,若 ReqA Commit 2 提交對就對 master 做 Rebase,之後用更新過的 shared.txt 開發,則 Commit 2、3 就不會繼續存入過期的 shared.txt,合併前的 Rebase 便不需解決 shared.txt 衝突,是一種平時多流汗,關鍵時刻少流血的概念。但若開發分支有 Push 到遠端,Rebase 後將無法再直接 Push,得改用 push -force 強制覆寫,這需要伺服器支援及額外設定,將增加管理複雜度。若採行合併前才 Rebase 策略,開發分支 Rebase 再併進 master 發即可刪除,就沒有 Rebase 後無法 Push 回遠端的困擾。

該直接合併還是先 Rebase 再合併?平日先 Rebase 還是留待合併前再 Rebase?不同做法各有優缺點,沒有標準答案,要考慮專案性質、開發人員習慣、管理制度要求.. 等因素自行拿捏。

Comparing difference between directly merging and rebasing before merging.


Comments

Be the first to post a comment

Post a comment