Git rebase - 變基

git rebase 是另一種整合分支的方式,和 merge 不同的是,它會重新整理 commit 歷史,讓歷史變得更線性、更乾淨。

Rebase vs Merge

Merge 的結果

main:     A --- B --- C --- M
                 \         /
feature:          D --- E

Merge 保留了分支的歷史,產生一個 merge commit (M)。

Rebase 的結果

main:     A --- B --- C
                       \
feature:                D' --- E'

Rebase 把 feature 的 commit 「重新播放」到 main 的最新位置,就像是從 C 開始開發的一樣。

基本用法

# 在 feature 分支上
git checkout feature

# 把 feature rebase 到 main 上
git rebase main

這會把 feature 的 commit 移到 main 的最新 commit 之後。

Rebase 的流程

  1. Git 找到兩個分支的共同祖先
  2. 把當前分支的 commit 暫時移除
  3. 把當前分支的指標移到目標分支
  4. 依序重新套用剛才移除的 commit
# 假設你在 feature 分支
git rebase main

# 步驟:
# 1. 找到 main 和 feature 的共同祖先 B
# 2. 把 feature 的 D、E commit 暫存
# 3. 把 feature 移到 main 的 C
# 4. 依序重新套用 D、E(變成 D'、E')

處理衝突

Rebase 過程中如果遇到衝突:

git rebase main
# CONFLICT: 衝突發生

# 1. 解決衝突
code conflicted-file.js

# 2. 標記已解決
git add conflicted-file.js

# 3. 繼續 rebase
git rebase --continue

如果想放棄 rebase:

git rebase --abort

如果想跳過這個 commit:

git rebase --skip

Interactive Rebase(互動式變基)

Interactive rebase 讓你可以在 rebase 過程中修改 commit。

git rebase -i HEAD~3

這會開啟編輯器,顯示最近 3 個 commit:

pick a1b2c3d Commit message 1
pick b2c3d4e Commit message 2
pick c3d4e5f Commit message 3

你可以修改每行開頭的指令:

指令說明
pick (p)保留這個 commit
reword (r)保留但修改訊息
edit (e)保留但暫停讓你修改
squash (s)和前一個 commit 合併
fixup (f)和前一個合併,但捨棄這個的訊息
drop (d)刪除這個 commit

合併多個 Commit

pick a1b2c3d Add feature
squash b2c3d4e Fix typo
squash c3d4e5f Add tests

這會把三個 commit 合併成一個。

修改 Commit 訊息

reword a1b2c3d Old message
pick b2c3d4e Another commit

Git 會讓你編輯第一個 commit 的訊息。

重新排序 Commit

只要調換行的順序就可以重新排序 commit:

pick c3d4e5f Third commit
pick a1b2c3d First commit
pick b2c3d4e Second commit

刪除 Commit

pick a1b2c3d Keep this
drop b2c3d4e Delete this
pick c3d4e5f Keep this too

常見使用情境

同步遠端更新

# 取得遠端更新
git fetch origin

# rebase 到遠端最新
git rebase origin/main

或者用 git pull --rebase

git pull --rebase origin main

整理 Commit 歷史

在 push 之前,整理混亂的 commit:

# 合併最近 5 個 commit
git rebase -i HEAD~5

移動分支

# 把 feature 從 develop 移到 main
git rebase --onto main develop feature

Rebase 的黃金法則

永遠不要 rebase 已經推送到公開 repository 的 commit。

因為 rebase 會改變 commit 的 hash,如果其他人已經基於這些 commit 開發,會造成嚴重的歷史混亂。

安全的 Rebase

# ✓ 可以:rebase 還沒 push 的 commit
git rebase main

# ✓ 可以:rebase 自己獨立的分支
git push --force-with-lease  # 比 --force 安全

危險的 Rebase

# ✗ 不要:rebase 已經推送的 commit
# ✗ 不要:rebase 多人協作的分支

Rebase vs Merge 的選擇

使用 Merge

  • 想保留完整的分支歷史
  • 多人協作的分支
  • 公開的分支(如 main、develop)

使用 Rebase

  • 想要乾淨的線性歷史
  • 個人的功能分支
  • 在 push 之前整理 commit

團隊建議

一個常見的做法:

  1. 開發時用 rebase 同步 main 的更新
  2. 合併回 main 時用 merge(或 squash merge)
# 開發中,同步 main 的更新
git checkout feature
git rebase main

# 完成後,merge 回 main
git checkout main
git merge --no-ff feature

設定 Pull 時自動 Rebase

# 只對當前 repository
git config pull.rebase true

# 全域設定
git config --global pull.rebase true

這樣 git pull 就會自動使用 rebase 而不是 merge。

復原 Rebase

如果 rebase 出問題,可以用 reflog 找回原本的狀態:

# 查看操作歷史
git reflog

# 找到 rebase 前的位置,例如 HEAD@{2}
git reset --hard HEAD@{2}