Skip to main content

使用變基 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 做了什麼:

  1. 找到共同祖先 (B)
  2. 找到需要被變基的提交並且暫存他們
    這些提交包含從共同祖先到「目前分支」之間的所有提交,並且剃除「目標分支」已經存在的提交 (此範例沒有被剃除的提交,暫存 A1 B1 C1)
  3. 將目標分支最後一個提交作為出發點,把暫存的提交逐個重演到目標分支後面 (接上後成為 A1' B1' C1')

所以總共只有三步驟,找到共同祖先,以祖先為起點開始尋找目標提交,重演這些提交。

重演 (replay)

不只是簡單的將提交複製貼上,而是會重新生成 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 從單字、處理方式到用途哪裡跟衍有關係,所以我投變基一票。

參考