Git 撤銷操作指南

在 Git 中做錯了事不用慌,這篇整理了各種「後悔藥」,根據不同情境選擇適合的撤銷方式。

撤銷工作目錄的變更

還原單一檔案

# Git 2.23+ 建議用法
git restore filename.js

# 舊版用法
git checkout -- filename.js

還原所有變更

# 還原所有已追蹤檔案的變更
git restore .

# 舊版用法
git checkout -- .

刪除未追蹤的檔案

# 先看看會刪除什麼
git clean -n

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

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

# 包含被忽略的檔案
git clean -fdx

撤銷暫存區的變更

取消暫存單一檔案

# Git 2.23+ 建議用法
git restore --staged filename.js

# 舊版用法
git reset HEAD filename.js

取消所有暫存

git restore --staged .
# 或
git reset HEAD

檔案會回到「已修改但未暫存」的狀態。

取消暫存並還原檔案

# 分兩步
git restore --staged filename.js
git restore filename.js

# 一步到位(舊版)
git checkout HEAD -- filename.js

撤銷 Commit

只撤銷 Commit,保留變更在暫存區

git reset --soft HEAD~1

適用於想重新編輯 commit 訊息或分開 commit。

撤銷 Commit,保留變更在工作目錄

git reset HEAD~1
# 或
git reset --mixed HEAD~1

適用於想重新選擇要 commit 的檔案。

完全撤銷 Commit 和變更

git reset --hard HEAD~1
小心:變更會完全消失!

撤銷已推送的 Commit(保留歷史)

git revert HEAD
git push

這會建立一個新的 commit 來反轉變更,安全且適合團隊協作。

撤銷 Merge

還沒完成的 Merge(有衝突時)

git merge --abort

已完成但還沒推送

git reset --hard HEAD~1

已經推送

git revert -m 1 <merge-commit-hash>
git push

撤銷 Rebase

Rebase 過程中

git rebase --abort

Rebase 完成後

# 用 reflog 找到 rebase 前的位置
git reflog
# 找到類似 "HEAD@{5}: rebase (start)" 的前一筆

git reset --hard HEAD@{5}

撤銷 Reset

用 Reflog 復原

# 查看 HEAD 的移動歷史
git reflog

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

撤銷 Stash

取消 stash apply

# 如果剛 apply,可以用 checkout 還原
git checkout -- .

# 或重新 stash
git stash

復原被刪除的 Stash

# 找出懸空的 commit
git fsck --no-reflog | grep commit

# 查看內容
git show <commit-hash>

# 建立新的 stash
git stash store <commit-hash>

快速查詢表

情境指令
還原工作目錄的變更git restore .
取消暫存git restore --staged .
撤銷最後一次 commit(保留變更)git reset --soft HEAD~1
撤銷最後一次 commit(不保留變更)git reset --hard HEAD~1
安全地撤銷已推送的 commitgit revert HEAD
取消 mergegit merge --abort
取消 rebasegit rebase --abort
修改最後一次 commitgit commit --amend
刪除未追蹤的檔案git clean -fd
復原任何操作git reflog + git reset

Reflog:終極後悔藥

git reflog 記錄了 HEAD 的每次移動,是找回「遺失」commit 的最後手段:

git reflog

輸出:

a1b2c3d HEAD@{0}: reset: moving to HEAD~1
b2c3d4e HEAD@{1}: commit: Add feature
c3d4e5f HEAD@{2}: commit: Initial commit

找到你想回去的位置,然後:

git reset --hard HEAD@{1}
# 或用 commit hash
git reset --hard b2c3d4e

Reflog 是本地的,不會推送到遠端。

Reflog 預設保留 90 天,之後會被清理。

最佳實踐

1. 經常 Commit

小步 commit 讓你有更多還原點可以選擇。

2. 推送前再三確認

一旦推送,修改歷史就會影響其他人。

3. 善用分支

在分支上實驗,搞砸了可以直接刪掉分支。

4. 重要操作前備份

# 建立備份分支
git branch backup-before-crazy-operation

5. 了解指令的影響

在執行破壞性指令前,先確認:

  • 這會改變歷史嗎?
  • 這會影響遠端嗎?
  • 這會刪除未 commit 的變更嗎?

真的救不回來了怎麼辦

如果 git reflog 也找不到,試試:

# 找出所有懸空的 commit
git fsck --lost-found

# 查看懸空的 commit
git show <dangling-commit-hash>

如果這也沒有,而且變更已經被 Git 垃圾回收清除了,那就真的沒辦法了。

這就是為什麼:

  1. 經常 commit
  2. 經常 push 到遠端
  3. 在重要操作前建立備份分支