HOWTO · Git

撤消本地倉庫中的最後一次 Git 提交

你可以撤消最後一次 git 提交併保留檔案更改。

本頁內容

若要撤銷本機提交,請依照你希望如何處理其中已追蹤的變更,使用 git reset 的適當模式:保留變更並維持暫存、保留變更但取消暫存,或捨棄變更。重設會將目前分支移回較早的提交。若提交已經分享,建議使用 git revert 以保留共享歷史。

選擇重設模式

提交會記錄一份快照;索引存放下一次提交要納入的快照,而工作樹則包含目前檢出的檔案。git reset 會將目前分支頂端移至指定目標。所選模式決定索引與工作樹是否也會變更:

  • --soft 會移動分支頂端,並讓索引與工作樹維持原狀。
  • --mixed 會移動分支頂端,並將索引重設為目標狀態,同時保留工作樹中的檔案。這是預設模式。
  • --hard 會移動分支頂端,並將索引及已追蹤檔案的工作樹內容一併重設為目標狀態。這可能會捨棄已追蹤的工作。

假設提交歷史為 A — B — C,目前檢出的是 C。重設至 HEAD~1 會將分支移至 B;所選模式則決定如何處理 C 帶來的變更。

撤銷最近一次提交並保留已暫存的變更

若要移除提交,但讓其中的變更留在索引中,以便再次提交或修改後再提交,請使用 --soft:

命令如下:git reset --soft HEAD~1。

執行後,HEAD 會指向前一個提交,而被移除提交中的變更仍會保持暫存。建立替代提交前,可用 git diff --cached 檢查這些變更。

撤銷最近一次提交並保留未暫存的變更

若要移除提交,並將其中已追蹤的變更留在工作樹中、但不放在索引裡,請使用 --mixed:

命令如下:git reset --mixed HEAD~1。

檔案仍是已追蹤狀態;相較於新的 HEAD,其差異會在 git status 中顯示為未暫存的修改。由於 --mixed 是預設模式,git reset HEAD~1 的效果相同。若你想重新檢查這些變更,或選擇性地將它們加入暫存,這種方式就很實用。

撤銷最近一次提交並捨棄已追蹤的變更

只有在確定要從索引與工作樹中捨棄已提交的變更時,才使用 --hard。它會將已追蹤檔案重設為目標提交的狀態,這些檔案尚未提交的變更也可能因此遺失。一般而言,未追蹤檔案會保留下來;但若未追蹤檔案或目錄阻擋了目標中的已追蹤路徑,這些阻擋項目可能會遭到刪除。執行命令前,請先用 git status 檢查,並保存任何可能還需要的內容。

命令如下:git reset --hard HEAD~1。

選擇提交目標並確認結果

HEAD~1 表示目前提交的第一個父提交;HEAD~2 表示往前兩個提交。請謹慎選擇目標,因為重設會將目前分支移至該處。你可以使用 git log --oneline --graph -5 檢視近期歷史。

接著重設至預定目標,例如 git reset --mixed HEAD~2。使用 git log --oneline -3 和 git status 確認分支與檔案狀態。

執行以下範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

#!/usr/bin/env bash
set -eu
repo=$(mktemp -d)
trap 'rm -rf "$repo"' EXIT
cd "$repo"
git init -q
git config user.name Example
git config user.email example@example.com
printf 'first\n' > note.txt
git add note.txt
git commit -qm 'Add note'
printf 'second\n' >> note.txt
git commit -qam 'Extend note'
git reset --mixed HEAD~1 >/dev/null
git status --short
 M note.txt

此腳本會建立暫存儲存庫、建立兩個提交,接著重設至較早的提交,同時讓最近一次變更保持未暫存,最後顯示狀態。腳本結束時,清理程序會移除這個暫存儲存庫。

如果提交已經分享

重設其他人已取得的提交會改寫分支歷史。若你推送重設後的分支,協作者可能需要調整自己的副本,而一般推送也可能遭到拒絕。對於已發布至共享分支的提交,git revert <commit> 會建立一個反向撤銷先前變更的新提交,而不會從歷史中移除原提交。改寫共享分支前,請先與協作者協調。