git revert - 還原 Commit

git revert 建立一個新的 commit 來「反轉」之前的 commit,是安全地撤銷變更的方式。

Revert vs Reset

最大的差別是:

  • Reset:直接刪除 commit,改變歷史
  • Revert:建立新的 commit 來撤銷,保留歷史
Reset 前:  A --- B --- C
Reset 後:  A --- B

Revert 前:  A --- B --- C
Revert 後:  A --- B --- C --- C'(反轉 C)

Revert 是安全的,因為它不會改變歷史,適合已經推送的 commit。

基本用法

還原最後一個 Commit

git revert HEAD

Git 會開啟編輯器讓你編輯 revert commit 的訊息,預設訊息是:

Revert "Original commit message"

This reverts commit a1b2c3d4e5f...

還原特定 Commit

git revert a1b2c3d

不開啟編輯器

git revert HEAD --no-edit

只做變更不 Commit

git revert HEAD --no-commit
# 或
git revert HEAD -n

這會把 revert 的變更放到暫存區,但不自動 commit,讓你可以做額外修改或合併多個 revert。

還原多個 Commit

一次還原一個

git revert HEAD
git revert HEAD~1
git revert HEAD~2

每個 revert 都會產生一個新的 commit。

還原一個範圍

# 還原從 HEAD~3 到 HEAD 的 commit(不含 HEAD~3)
git revert HEAD~3..HEAD

# 還原並合併成一個 commit
git revert HEAD~3..HEAD --no-commit
git commit -m "Revert last 3 commits"

還原 Merge Commit

Merge commit 有兩個父 commit,revert 時需要指定要保留哪一邊:

# -m 1 保留第一個父 commit(通常是被合併到的分支)
git revert -m 1 <merge-commit>

# -m 2 保留第二個父 commit(被合併進來的分支)
git revert -m 2 <merge-commit>

假設你把 feature merge 到 main:

main:    A --- B --- M(merge commit)
               \   /
feature:        C

要撤銷這個 merge,保留 main 的 B:

git revert -m 1 M
還原 merge commit 後,如果之後想重新 merge 那個分支,需要先 revert 這個 revert。否則 Git 會認為那些變更已經被處理過了。

處理衝突

Revert 也可能遇到衝突:

git revert a1b2c3d
# CONFLICT: 衝突發生

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

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

# 3. 繼續 revert
git revert --continue

放棄 revert:

git revert --abort

實際範例

情境一:撤銷最近的錯誤 Commit

# 有問題的 commit 已經推送
git revert HEAD
git push

情境二:撤銷中間的某個 Commit

# 假設 commit 歷史是 A-B-C-D,想撤銷 B
git log --oneline
# d4e5f6g D - Latest
# c3d4e5f C
# b2c3d4e B - 有問題的 commit
# a1b2c3d A

git revert b2c3d4e

情境三:撤銷整個功能(多個 Commit)

# 假設最後 5 個 commit 都是某個功能
git revert HEAD~5..HEAD --no-commit
git commit -m "Revert: Remove feature X"

情境四:撤銷已合併的 Pull Request

# 找到 merge commit
git log --merges

# 還原 merge(保留 main 分支的狀態)
git revert -m 1 <merge-commit-hash>
git push

情境五:重新加回被 Revert 的變更

# 假設之前 revert 了 commit A
# 現在想把 A 的變更加回來

# 方法一:revert 那個 revert commit
git revert <revert-commit-hash>

# 方法二:cherry-pick 原本的 commit
git cherry-pick <original-commit-hash>

Revert vs 其他撤銷方式

什麼時候用 Revert

  • Commit 已經推送到遠端
  • 想保留完整的歷史紀錄
  • 團隊協作,不想影響其他人

什麼時候用 Reset

  • Commit 還沒推送
  • 只在本地開發
  • 想完全清除某些 commit

什麼時候用 Checkout

  • 只想查看之前的狀態
  • 不想修改任何東西

查看 Revert 的效果

在 revert 前,可以先看看會有什麼變化:

# 查看某個 commit 的內容
git show a1b2c3d

# 模擬 revert(不實際執行)
git revert a1b2c3d --no-commit
git diff --staged
git revert --abort

小技巧

批次 Revert 合併成一個 Commit

git revert HEAD~3..HEAD --no-commit
git commit -m "Revert: Remove problematic changes"

使用 dry-run 測試

Git revert 沒有 dry-run 選項,但你可以:

# 先在新分支測試
git checkout -b test-revert
git revert a1b2c3d

# 確認沒問題後,在原分支執行
git checkout main
git revert a1b2c3d

# 刪除測試分支
git branch -D test-revert