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 |
| 安全地撤銷已推送的 commit | git revert HEAD |
| 取消 merge | git merge --abort |
| 取消 rebase | git rebase --abort |
| 修改最後一次 commit | git 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 垃圾回收清除了,那就真的沒辦法了。
這就是為什麼:
- 經常 commit
- 經常 push 到遠端
- 在重要操作前建立備份分支