使用變基 Rebase 合併分支提交
筆者認為網路文章每篇講的都不一樣是初學者對變基 (rebase) 感到畏懼的原因,所以撰寫時同時參考 Git 官方文檔以及 Pro Git Book,多方比對和驗證保證本文解釋方式能和指令實際用途能夠對應,講這麼多目的就是要讓你只要讀這篇文章就夠了,不需要再去網路上查其他文章,因為網路上的文章參差不齊容易被誤導。
Rebase 很危險,請用範例 repo 進行測試!
什麼是變基 Rebase
介紹 rebase 前我們要了解 merge。merge 是把兩個分支合併成一個分支,以下方原始狀態為例,使用 git merge 會產生新的提交 F 紀錄合併,並且保留分支結構:
# 原始狀態
main A---B---C---D---E
\
feature A1--B1--C1
# 使用 git merge,指令如下:
# git checkout main # 切換分支到 main
# git merge feature # 合併 feature 分支
main A---B---C---D---E---F
\ /
feature A1---B1----C1
但如果我們想保持提交樹的乾淨,這時候我們就可以使用 git rebase
完成。在原始狀態使用 git rebase 效果如下:
# 使用 git rebase,指令如下:
# git checkout feature # 切換分支到 feature
# git rebase main # 目標分支是 main
main A---B---C---D---E
\
feature A1'--B1'--C1'
可以看到目前分支 (feature) 被接在目標分支 (main) 的後面而且沒有用於紀錄合併的提交。細心的人可能會發現多了 prime symbol '
,這裡我們詳細解釋 git rebase main
做了什麼:
- 找到共同祖先 (B)
- 找到需要被變基的提交並且暫存他們
這些提交包含從共同祖先到「目前分支」之間的所有提交,並且剃除「目標分支」已經存在的提交 (此範例沒有被剃除的提交,暫存 A1 B1 C1) - 將目標分支最後一個提交作為出發點,把暫存的提交逐個重演到目標分支後面 (接上後成為 A1' B1' C1')
所以總共只有三步驟,找到共同祖先,以祖先為起點開始尋找目標提交,重演這些提交。
不只是簡單的將提交複製貼上,而是會重新生成 commit hash。
口訣
re-base 的核心兩個單字分別代表 重新
以及 基底
,表示此指令用於幫提交修改基底。第一次看到覺得很複雜是正常的(主要是因為網路太多錯誤資訊),可以用口訣將 rebase 理解為
請務必記住口訣,這可以讓你正常使用好一段時間,筆者保證這段敘述的絕對正確,所有違背這段敘述的說明都是錯的。
口訣有稍微簡化,完整版本請見搞懂 Rebase Onto。
誰 Rebase 誰才對?
很多文章都錯誤使用 rebase,小到個人 medium 和部落格,大到系列教學文章,甚至是已經出書的人都寫錯,不就是只有一個參數的指令那究竟是哪裡用錯呢?他們錯在位於主分支使用 git rebase feature
,這會造成主分支的提交歷史改變,而主分支是最穩定的分支,絕對不可能為了合併子分支而修改主分支既存的提交歷史。
正確的使用方式應該是先移動到要被合併的分支,再使用 git rebase main
:
# 這四個指令代表把 feature-1 和 feature-2 在 main 之後進行重演
git switch feature-1
git rebase main
git switch feature-2
git rebase main
# 或者使用兩個參數,第二個參數代表預先 switch 到該分支,效果和上面的指令完全相同
git rebase main feature-1
git rebase main feature-2
只要看懂文檔任何人就不會犯這種錯,因為文檔就是這樣用的,很可惜大家都錯了。如果你不相信筆者,那麼請看 官方文檔 或是教學書 Pro Git,如果還是很疑惑,那麼 Python Core Dev,微軟工程師拍的影片十分钟学会正确的github工作流,和开源作者们使用同一套流程也是這樣用。
絕對不要在主分支上 rebase 其餘分支,因為這會修改穩定的主分支提交紀錄。
git rebase --onto
過於複雜,他可以直接寫成一篇文章,請看 搞懂 Rebase Onto。
翻譯:變基和衍合
rebase 中文有變基和衍合兩種翻譯,衍代表散佈、滋生,我看不出來 rebase 從單字、處理方式到用途哪裡跟衍有關係,所以我投變基一票。