04 --- 安全撤销:改错了怎么退回去
摘要 :本文系统讲解 Git 撤销操作的四种核心场景:1) 撤掉工作区未提交的改动(git restore);2) 取消暂存(git restore --staged);3) 修改最近一次提交(git commit --amend);4) 退回历史版本(git reset 的三种模式)。通过三区模型(工作区、暂存区、仓库)理解撤销的本质,强调「先问退哪一块,再选命令」的原则,重点区分 --soft、--mixed、--hard 的差异与风险,并提供安全操作习惯与真实场景示例,帮助读者安全、精准地撤销错误操作。
写在前面:这一章要解决什么
只要你用 Git,就一定会改错。常见的几种「错」:
- 改了一堆,发现走错方向了,想丢掉工作区的改动
- 暂存了不该暂存的文件,想从篮子里拿出来
- 刚提交完发现说明写错字了,想改
- 想退到几个提交之前的状态
Git 给了好几种「撤销」,不同命令退不同地方。用错命令可能把工作白干没了。这一章教你分清。
学完后,你应该能:
- 看着「我想退哪里」选出对的命令
- 分清
restore、reset、amend各自的边界 - 知道
reset --hard危险在哪,什么时候才用 - 改最近一次提交的说明或内容
读者设定: 大一同学,会三区模型和基本工作流,还没碰过远程。
1. 定位:为什么「撤销」要单开一章
1.1 一句话先记住
撤销不是一招,是几招;每招只退三区里的一块或几块。
回顾三区(第 02 章):

图:工作区 / 暂存区 / 仓库。撤销就是「让哪一块退回老样子」。
1.2 不同错误对应不同招式
| 你想干的事 | 用什么 |
|---|---|
| 丢掉工作区里没提交的改动 | git restore 文件 |
| 把文件从篮子拿出来(保留改动) | git restore --staged 文件 |
| 改最近一次提交的说明或内容(没推送) | git commit --amend |
| 把分支指针退到某个旧提交 | git reset(有三种力度) |
| 已经推送给别人了的坏提交 | git revert(进阶章再细讲) |
关键原则: 先问「我要退哪一块」,再选命令。
1.3 和你已经会的对比
| 你已经会的 | Git 对应 |
|---|---|
| 编辑器「撤销」 | git restore(只动工作区) |
| 「另存为」回到旧版 | git reset --hard(强力,慎用) |
| 把草稿从信封里抽出来 | git restore --staged |
| 改一下刚发的朋友圈文案 | git commit --amend |
1.4 本章内容目标
| 目标 | 你能做到 |
|---|---|
| 撤工作区 | 会用 git restore 丢掉未提交改动 |
| 取消暂存 | 会用 git restore --staged |
| 改最近提交 | 会用 git commit --amend |
| 退版本 | 看懂 reset 三种模式的区别 |
| 不闯祸 | 知道 --hard 危险,先备份 |
2. 本质:撤销到底在动什么
2.1 三种 reset 模式(核心图)

图:--soft 只动 HEAD;--mixed 还动暂存区;--hard 三块都动。绿色对勾=被改回目标版本,灰色横线=不动。
这张图是本章的灵魂,看懂它就懂了一大半。
2.2 用白话拆开
git reset 的本质是 把分支指针(HEAD)移到另一个提交。然后根据模式决定要不要把暂存区、工作区也一起「拉回」那个版本:
| 模式 | HEAD 移动吗 | 暂存区 | 工作区 |
|---|---|---|---|
--soft |
移 | 不动 | 不动 |
--mixed(默认) |
移 | 拉回 | 不动 |
--hard |
移 | 拉回 | 拉回 |
--hard 危险:它会丢掉工作区里没提交的改动。除非你确定不要了,否则别用。
2.3 几条硬规矩
| 规矩 | 白话 |
|---|---|
restore 默认只动工作区 |
撤掉你没提交的编辑改动 |
restore --staged 只动篮子 |
把文件从篮子拿出来,改动还在 |
amend 改的是「最近一次」提交 |
只在没推送给别人时安全 |
reset --hard 会清工作区 |
用前先确认没有未保存的宝贝 |
| 已推送的历史慎改 | 别人已经基于它工作了 |
2.4 新手最常踩的坑
| 坑 | 后果 | 预防 |
|---|---|---|
一上来就 reset --hard |
工作区改动没了 | 先 status,能用 restore 就别上 --hard |
amend 已推送的提交 |
别人拉到旧版会乱 | 推过就别 amend,用 revert |
| 以为撤销 = 上传撤销 | 本地改了,远程没动 | 远程撤销是后面章节 |
把 restore 和 reset 搞混 |
一个动文件内容,一个动分支指针 | 看准「退哪一块」 |
3. 建议学习顺序
text
先会 restore(最常用、最安全)
→ 再会 restore --staged(取消暂存)
→ 再会 commit --amend(改最近提交)
→ 最后学 reset 三模式(退版本)
→ 永远记住:--hard 是核武器
4. 动手准备
bash
mkdir lab-undo
cd lab-undo
git init -b main
git config user.name "Ada Example"
git config user.email "ada@example.com"
先做一次提交当起点:
bash
printf 'v1\n' > app.txt
git add app.txt
git commit -m "feat: v1"
5. 跟着做:四种撤销
5.1 撤掉工作区改动(最常用)
故意把文件改坏:
bash
printf '改坏了\n' > app.txt
git status
你会看到 app.txt 在「尚未暂存」一类。
现在丢掉这次改动,恢复成上次提交的样子:
bash
git restore app.txt
cat app.txt
text
v1
白话翻译: git restore 把工作区里这个文件 拉回成暂存区/HEAD 的版本。你没提交的改动没了。
| 命令 | 副作用 |
|---|---|
git restore <文件> |
丢弃该文件未提交的改动(危险:丢了就没了) |
| 是否动暂存区 | 不动 |
| 是否动历史 | 不动 |
小提示:
restore是较新的命令。老资料里用git checkout -- 文件,意思一样。
5.2 取消暂存(从篮子里拿出来)
把文件改进篮子,又后悔:
bash
printf 'v2\n' > app.txt
git add app.txt
git status
这时 app.txt 在「将要提交」一类(已在篮子里)。
把它从篮子拿出来,保留工作区的改动:
bash
git restore --staged app.txt
git status
text
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.txt
白话翻译: 文件回到「改了但没暂存」状态;改动本身没丢。
| 命令 | 副作用 |
|---|---|
git restore --staged <文件> |
取消暂存;工作区改动保留 |
| 是否丢内容 | 不丢 |
5.3 改最近一次提交(amend)
场景:刚提交完,发现说明写错字,或忘了加一个文件。
先做一次提交:
bash
printf 'v2\n' > app.txt
git add app.txt
git commit -m "feat: v2"
发现说明想改:
bash
git commit --amend -m "feat: 把版本升到 v2"
白话翻译: amend 不新建提交,而是 替换 最近一次提交(其实是新建一个提交顶替旧位置)。
| 命令 | 副作用 |
|---|---|
git commit --amend -m "新说明" |
改最近一次提交的说明 |
git commit --amend --no-edit |
把当前暂存内容并入最近提交,说明不变 |
| 安全前提 | 这次提交还没推送给别人 |
重要:已推送的提交不要 amend 。别人已经基于旧哈希工作了,你一改哈希就乱套。已推送的坏提交要用后面的
revert。
5.4 退版本:reset 三种模式
先造一条小历史:
bash
printf 'v3\n' > app.txt
git add app.txt
git commit -m "feat: v3"
git log --oneline
text
........ feat: v3
........ feat: v2
........ feat: v1
(1)--soft:只动指针,篮子和书桌都不动
bash
git reset --soft HEAD~1
git status --short
text
M app.txt
白话翻译: HEAD 退一格(v3 那笔没了),但 app.txt 的 v3 内容 还在篮子里 。第一列 M 表示已暂存。
适合:你提交早了,想再凑点东西一起重新提交。
(2)--mixed(默认):动指针 + 把篮子拉回
bash
git reset --mixed HEAD~1
git status --short
text
M app.txt
白话翻译: HEAD 退一格,篮子也清空;改动 还在工作区 (第二列 M)。
这是 reset 不加参数的默认行为。
适合:你暂存错了,想重新挑选要提交什么。
(3)--hard:三块全拉回(危险)
bash
# 危险!先确认没有未保存的宝贝
printf '丢了?\n' > app.txt
git add app.txt
git commit -m "feat: 将被 hard reset 掉"
git reset --hard HEAD~1
text
HEAD is now at ........ feat: v3 mixed
白话翻译: HEAD 退一格,篮子清空,工作区也回到目标版本。刚提交的「丢了?」那笔从分支上看不见了。
别慌:被
--hard丢掉的提交,短时间内还能用git reflog找回(进阶章节讲)。但工作区里没提交的改动是真的没了。

图:再看一遍------绿色对勾=被改回,灰色横线=不动。
6. 命令按「用途」分组
6.1 撤工作区(小心,丢内容)
| 命令 | 干什么 |
|---|---|
git restore <文件> |
丢掉该文件未提交改动 |
git restore . |
丢掉所有未提交改动(更危险) |
6.2 取消暂存(不丢内容)
| 命令 | 干什么 |
|---|---|
git restore --staged <文件> |
从篮子拿出,保留改动 |
git restore --staged . |
把所有暂存都撤掉 |
6.3 改最近提交
| 命令 | 干什么 |
|---|---|
git commit --amend -m "新说明" |
改说明 |
git commit --amend --no-edit |
并入暂存,说明不变 |
6.4 退版本(动分支指针)
| 命令 | 干什么 | 危险度 |
|---|---|---|
git reset --soft <目标> |
只动指针 | 低 |
git reset --mixed <目标> |
动指针 + 清篮子 | 低 |
git reset --hard <目标> |
动指针 + 清篮子 + 清工作区 | 高 |
7. 对照表:降低记忆负担
7.1 「我想干啥」决策表
| 想干啥 | 用什么 |
|---|---|
| 丢掉工作区改动 | git restore 文件 |
| 从篮子拿出文件 | git restore --staged 文件 |
| 改最近提交说明 | git commit --amend -m "..." |
| 退一格但保留改动 | git reset --mixed HEAD~1 |
| 退一格且全清干净 | git reset --hard HEAD~1(危险) |
| 撤销已推送的提交 | git revert(进阶章) |
7.2 三种 reset 一次记牢
| 模式 | 指针 | 篮子 | 书桌 |
|---|---|---|---|
--soft |
动 | 不动 | 不动 |
--mixed |
动 | 动 | 不动 |
--hard |
动 | 动 | 动 |
记忆口诀:软最轻、混合中、硬最重。
7.3 restore vs reset 别搞混
restore |
reset |
|
|---|---|---|
| 主要动什么 | 文件内容 | 分支指针 |
| 会移动 HEAD 吗 | 不会 | 会 |
| 危险度 | 较低 | --hard 高 |
8. 安全习惯
8.1 建议这样做
| 习惯 | 原因 |
|---|---|
撤销前先 git status |
看清现在哪一块脏 |
| 优先用最轻的招 | 能 restore 就别 reset --hard |
--hard 前先备份 / 提交 |
工作区改动丢了不好找 |
amend 只用于未推送的提交 |
别破坏共享历史 |
学会 reflog(进阶) |
--hard 丢的提交还能救 |
8.2 推荐决策流
text
只想撤工作区编辑? → git restore 文件
暂存错了? → git restore --staged 文件
最近提交说明写错? → git commit --amend
想退到旧版本、保留改动? → git reset --mixed HEAD~1
想退到旧版本、全清? → git reset --hard HEAD~1(先确认!)
已经推送的坏提交? → git revert(后面学)
9. 真实场景
9.1 改了一晚上发现走错方向
bash
git status # 看清改了哪些
git restore . # 丢掉所有未提交改动(确认后)
或只丢部分文件:git restore 文件A 文件B。
9.2 暂存了不该暂存的文件
bash
git restore --staged 那个文件
改动还在,只是从篮子里拿出来了。
9.3 刚提交完发现说明写错字
bash
git commit --amend -m "正确的说明"
前提:这次提交还没推送。
9.4 想回到「昨天那个能跑的版本」
bash
git log --oneline # 找到那次的短哈希
git reset --mixed <哈希> # 退回去,改动保留在工作区
想彻底回到那个版本(丢掉之后的改动):
bash
git reset --hard <哈希> # 危险,先确认
9.5 已经推送的坏提交
别 reset。用 git revert(进阶章)新建一笔「反向」提交,公开撤销。
10. 稍微多懂一点点(可选)
reflog是后悔药 :reset --hard丢的提交,本地 reflog 里还记着,短时间内能找回。进阶章细讲。amend其实是新建提交 :底层是新建一个提交顶替旧位置,旧提交对象还在.git里待一阵。reset移动的是分支指针 :分支名(如main)指向某个提交;reset就是改这个指向。restore是较新命令 :老资料用git checkout --,意思一样;新命令更清楚。
11. 小实验(请一定动手)
实验甲:撤工作区
- 建仓,提交
v1。 - 改文件成「乱改」。
git restore恢复,确认回到v1。
实验乙:取消暂存
- 改文件,
git add。 git restore --staged,确认改动还在工作区。
实验丙:改最近提交
- 提交一次,说明写「写错了」。
git commit --amend -m "正确的",看git log --oneline。
实验丁:reset 三模式
- 造三次提交
v1 v2 v3。 git reset --soft HEAD~1,看status --short(第一列 M)。- 重新提交后再
git reset --mixed HEAD~1,看(第二列 M)。 - 再提交后
git reset --hard HEAD~1,看工作区也干净了。
通过标准: 能说出三种模式各动了哪几块。
12. 常见问题
问 1:reset --hard 丢了能找回吗?
短时间内可以,用 git reflog(进阶章)。但工作区里没提交的改动是真的没了。
问 2:amend 和 revert 什么区别?
amend 改最近提交(替换,适合未推送);revert 新建一笔反向提交(适合已推送)。
问 3:撤销会上传撤销吗?
不会。本章所有命令只动本地。远程撤销是后面章节。
问 4:restore 和 checkout 一样吗?
restore 是新命令,专管恢复文件;checkout -- 是老写法,意思相近。
问 5:我可以天天 reset --hard 吗?
不推荐。--hard 会清工作区,养成习惯容易丢东西。优先用最轻的招。
问 6:退到旧版本后,之后的提交还在吗?
还在 .git 对象库里(一段时间内),但分支指针不指它们了。reflog 能看到。
问 7:HEAD~1 是什么?
「当前提交的上一笔」。HEAD~2 是上上笔,以此类推。
问 8:改了已推送的提交怎么办?
别 amend。用 git revert 公开撤销,后面章节细讲。
13. 总结、学习路线与思维升华
13.1 这一章请记住的
| 点 | 记住什么 |
|---|---|
| 撤销分招 | 每招退不同地方 |
| 最常用 | git restore 撤工作区 |
| 取消暂存 | git restore --staged |
| 改最近提交 | git commit --amend(未推送才行) |
| 退版本 | reset 三模式:软/混合/硬 |
| 危险 | --hard 清工作区,慎用 |
| 后悔药 | reflog(进阶章) |
13.2 在整个系列中的位置
text
01 认识 Git
02 三区模型
03 基本工作流
04 安全地撤销 ← 当前
05 分支与合并
06 远程协作
......
13.3 思维升华
撤销不是一招,是「退哪一块」的学问。
先问自己:我要退工作区、篮子,还是分支指针?
选对招式,比按一百次 Ctrl+Z 安全。
13.4 参考资料
以下资料帮助校对了本章难度与术语。
- Pro Git 中文版 --- 撤消操作
- Pro Git 中文版 --- 重置揭密
- git restore 说明
- git reset 说明
- git commit 说明
- 本仓库图示署名:
assets/diagrams/ATTRIBUTION.md
命令输出样例验证环境:Git 2.43.0 ;演示作者信息为虚构:Ada Example <ada@example.com>。
13.5 本章检查清单
- 会用
git restore撤工作区改动 - 会用
git restore --staged取消暂存 - 会用
git commit --amend改最近提交说明 - 能说出
reset三种模式各动哪几块 - 知道
--hard危险,用前会先确认 - 知道已推送的提交不要
amend - 听说过
reflog能救--hard丢的提交
改错不可怕,可怕的是用错撤销招式。下一章我们学分支:怎么在不破坏主线的情况下试新想法。