git reset - 重置 Commit

git reset 用來移動 HEAD 和分支指標,可以用來取消 commit、取消暫存,或回到之前的狀態。

Reset 的三種模式

Git reset 有三種模式,影響的範圍不同:

--soft   只移動 HEAD,保留暫存區和工作目錄
--mixed  移動 HEAD,重置暫存區,保留工作目錄(預設)
--hard   移動 HEAD,重置暫存區和工作目錄

git reset --soft

git reset --soft HEAD~1
  • 取消最後一個 commit
  • 變更保留在暫存區
  • 工作目錄不變

適用情境:想重新編輯 commit 訊息或合併 commit。

# 取消 commit 但保留變更在暫存區
git reset --soft HEAD~1

# 重新 commit
git commit -m "New message"

git reset --mixed(預設)

git reset HEAD~1
# 等同於
git reset --mixed HEAD~1
  • 取消最後一個 commit
  • 變更移出暫存區,回到工作目錄
  • 工作目錄不變

適用情境:想重新選擇要 commit 的檔案。

# 取消 commit,變更回到工作目錄
git reset HEAD~1

# 只加入部分檔案
git add specific-file.js
git commit -m "Commit only specific files"

git reset --hard

git reset --hard HEAD~1
  • 取消最後一個 commit
  • 重置暫存區
  • 重置工作目錄(變更會遺失
小心使用 --hard,未 commit 的變更會永久遺失!

適用情境:想完全回到之前的狀態,不要任何變更。

Reset 的目標

回到前幾個 Commit

# 回到上一個 commit
git reset HEAD~1

# 回到前三個 commit
git reset HEAD~3

# 回到特定 commit
git reset a1b2c3d

使用 Commit Hash

git reset --hard a1b2c3d4e5f

使用分支名稱

git reset --hard origin/main

常見用法

取消暫存

# 取消所有暫存
git reset

# 取消特定檔案的暫存
git reset HEAD index.html

# Git 2.23+ 建議使用
git restore --staged index.html

修改最後一個 Commit

# 方法一:reset 後重新 commit
git reset --soft HEAD~1
# 修改後重新 commit
git commit -m "New message"

# 方法二:使用 amend(更簡單)
git commit --amend -m "New message"

合併多個 Commit

# 假設要合併最後三個 commit
git reset --soft HEAD~3
git commit -m "Combined commit message"

完全回到遠端的狀態

git fetch origin
git reset --hard origin/main

這會讓本地的 main 和遠端的 origin/main 完全一樣。

取消所有本地變更

# 取消所有變更,回到最後一次 commit
git reset --hard HEAD

# 或
git checkout -- .

Reset vs 其他指令

Reset vs Revert

ResetRevert
修改歷史
產生新 commit
適合已推送的 commit不適合適合
# Reset:移除 commit(會改變歷史)
git reset --hard HEAD~1

# Revert:產生反向 commit(不改變歷史)
git revert HEAD

Reset vs Checkout

# Reset:移動分支指標
git reset HEAD~1

# Checkout:移動 HEAD,不動分支
git checkout HEAD~1  # 進入 detached HEAD 狀態

復原 Reset

如果 reset 錯了,可以用 reflog 找回:

# 查看 HEAD 移動歷史
git reflog

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

這是為什麼即使用了 --hard,commit 通常還是可以救回來。

實際範例

情境一:Commit 了不該 commit 的檔案

# 取消最後一次 commit,但保留變更
git reset --soft HEAD~1

# 移除不該 commit 的檔案
git reset HEAD secret.env

# 重新 commit
git commit -m "Original message"

情境二:想重做最後幾個 Commit

# 回到三個 commit 之前,保留變更
git reset --mixed HEAD~3

# 重新整理並 commit
git add .
git commit -m "Refactored commit"

情境三:本地分支搞砸了

# 直接回到遠端的狀態
git fetch origin
git reset --hard origin/main

情境四:取消 Merge

# 如果還沒 commit(merge 進行中)
git merge --abort

# 如果已經 commit
git reset --hard HEAD~1

注意事項

不要 reset 已推送的 commit

Reset 會改變歷史,如果你 reset 了已經推送的 commit,然後要推送:

git push --force

這會覆蓋遠端的歷史,可能影響其他協作者。

如果 commit 已經被推送並有其他人基於它開發,請用 git revert 而不是 git reset

未追蹤的檔案

git reset 不會影響未追蹤的檔案。要清除它們:

# 查看會被清除的檔案
git clean -n

# 清除未追蹤的檔案
git clean -f

# 清除未追蹤的檔案和目錄
git clean -fd