- 基础篇01: 【Git 基础】01------代码是怎么被追踪的
- 基础篇02: 【Git基础】02------分支:在不破坏主线的情况下做实验
写代码总会手滑。改了半天发现方向错了,或者 commit 信息写错了,甚至把不该提交的东西提交上去了,这些都是日常。Git 的撤销体系看起来命令很多,但核心其实只有一句话:先判断改动在哪一层,再选择对应的撤销工具。只要这点清楚,Git 的"后悔药"其实非常好用。
引言
先建立一张地图,Git 追踪改动分三层:
-
工作区(Working Directory):你正在编辑的文件
-
暂存区(Staging Area) :执行
git add之后 -
提交历史(Commit History) :执行
git commit之后
撤销操作的方向,本质就是沿着这条链 向后退。

三层结构图:工作区、暂存区、提交历史依次排列,撤销操作是沿着这条链往回走的过程。
一、场景一:还没 add,直接丢弃工作区改动
你改了一个文件,还没 add,现在想把它恢复到上次提交的状态:
bash
git restore README.md
这条命令会丢弃工作区的改动 ,文件直接回到最近一次 commit 的版本。注意,这个操作是不可逆的,因为工作区的修改没有进入Git历史。
旧版 Git 用的是 git checkout -- README.md,效果一样,只是语义不够清晰。现在推荐统一用 git restore。
二、场景二:已经add,但还没 commit
git add 之后反悔了,想把文件从暂存区拿回来:
bash
git restore --staged README.md
加了 --staged 就是针对暂存区操作。执行后,文件回到"已修改但未暂存"的状态,工作区的改动还在,只是不再准备提交了。
如果想一步到位,既撤销暂存,又丢弃工作区修改,推荐分两步:
bash
git restore --staged README.md
git restore README.md
意图清晰比命令短更重要。
三、场景三:已 commit,怎么办
这里是 Git 新手最容易混乱的地方
这里要区分两种情况:这个 commit 有没有推送到远程。
3.1 没推送:可以用 git reset
git reset 会移动HEAD指针,改写提交历史。
有三种模式:
| 模式 | 改动去向 |
|---|---|
--soft |
回到暂存区 |
| 默认(mixed) | 回到工作区 |
--hard |
直接丢弃 |
(1) commit -> 暂存区
bash
git reset --soft HEAD~1
(2) commit -> 工作区
bash
git reset HEAD~1
(3) commit 直接删除
bash
git reset --hard HEAD~1

三个模式的区别在于改动去哪里:--soft 退回暂存区,默认(--mixed)退回工作区,--hard 直接扔掉。--hard 用之前要想清楚,它是真正的删除按钮,改动没有备份,找回来非常麻烦。
3.2 已推送:必须用 git revert
一旦 commit 推送到了远程,绝对不要用 git reset。原因很简单:远程已经有这个commit的记录,你本地改写历史之后,推送会被拒绝;就算强推成功,协作的同事拉代码时会遭遇历史冲突,现场一片混乱。
正确做法是 git revert:
bash
git revert HEAD
它不会删除commit,而是会生成一个新的 commit,内容是把目标 commit 的改动反向应用一遍。历史链条不动,安全,可追溯。
提交历史:A → B → C(需要撤销 C)
revert 后:A → B → C → C'(C' 是 C 的逆操作)

reset 直接把 HEAD 往回挪,C 从历史中"消失",本地没问题,但如果这个 commit 已经推送到远程,别人的历史里还有 C,强推会导致冲突。而revert 则是在 C 后面追加一个反向 commit C',历史只增不删,对协作者完全透明,这就是为什么已推送的 commit 必须用 revert。
代价是历史会多一条记录,但这正是团队协作里应有的透明度。
四、git stash:保存现场,去干别的
有一种场景 reset 和 revert 都处理不了:你改到一半,突然需要切分支处理紧急 bug。改动还没到能提交的程度,但直接切分支又可能出现冲突或者把改动带过去。
这时候可以用 git stash:
bash
git stash
它会把当前工作区和暂存区的改动打包藏起来,让你的工作目录回到干净状态。处理完紧急任务,切回原来的分支,再把现场恢复出来:
bash
git stash pop
pop 会把最近一次 stash 的内容恢复,并从 stash 列表里移除。如果想保留记录,用 git stash apply 代替。
有时候同时有几个 stash,可以用 git stash list 查看,git stash pop stash@{1} 指定恢复哪一个。
五、决策表:碰到问题查这里
| 当前状态 | 目标 | 命令 |
|---|---|---|
| 工作区有改动,未 add | 丢弃改动 | git restore <file> |
| 已 add,未 commit | 撤销暂存 | git restore --staged <file> |
| 已 commit,未推送 | 撤销 commit | git reset HEAD~1 |
| 已 commit,已推送 | 撤销 commit | git revert <commit> |
| 改到一半要切分支 | 临时保存现场 | git stash / git stash pop |
撤销体系的核心逻辑就是一句话:改动走到哪一层,用对应层的工具往回退;已经共享出去的历史,只能用新 commit 来修正,不能改写。