改错了想撤销,一搜全是 git reset --hard,照着敲完代码全没了------这大概是 Git 新手最痛的一课。问题不在命令,在于没分清"撤销什么" :是工作区的改动、暂存区的内容、还是已经提交甚至已经推送的 commit?场景不同,命令完全不同。本文按场景把 restore / reset / revert / amend 一次讲清,每条都标了会不会丢代码,照着选就行。
一、先分清三个区
Git 的改动会经过三个地方,撤销就是把改动从某个区拿回来:
- 工作区 :你正在编辑的文件(还没
git add)。 - 暂存区 :
git add之后、git commit之前。 - 版本库 :
git commit之后的历史。
搞错在哪个区,是所有撤销翻车的根源。下面分场景。
二、撤销工作区的改动(还没 git add)
改乱了想还原成上次提交的样子:
bash
git restore 文件名 # 还原单个文件
git restore . # 还原当前目录所有改动(谨慎,未保存的编辑会没)
git restore 是新版推荐写法;老教程里的 git checkout -- 文件名 等价,但 checkout 语义太杂,能用 restore 就用它。
三、撤销暂存(git add 了,还没 commit)
git add 加错了文件,想从暂存区撤回(但保留你的改动):
bash
git restore --staged 文件名 # 取消暂存,改动还在工作区
等价的老写法是 git reset HEAD 文件名。注意这一步不会丢代码,只是把文件退回"已修改未暂存"状态。
四、撤销 / 修改最后一次 commit
- 只想改提交信息或漏加文件:
bash
git commit --amend # 修改最后一次 commit(信息 + 可补 add 的文件)
- 撤销最后一次 commit,但保留改动(最常用、最安全):
bash
git reset --soft HEAD~1 # commit 撤销,改动回到暂存区
- 撤销最后一次 commit 并丢弃改动(危险,确认不要了再用):
bash
git reset --hard HEAD~1 # 改动直接没,慎用
记一个口诀:--soft 留改动、--hard 删改动。
五、回退历史版本:reset 还是 revert?
这是最容易出事的地方,关键看有没有推送给别人:
- 本地、没 push :用
reset直接把分支指针挪回去。
bash
git reset --hard 提交哈希 # 本地回退到指定版本
- 已经 push、别人可能拉过 :别用 reset ,用
revert生成一个"反向提交",历史向前而不是被改写。
bash
git revert 提交哈希 # 新增一个抵消该提交的 commit,安全可推送
一句话:reset 改写历史(私有分支用),revert 追加历史(公共分支用)。
六、已经 push 了还想撤,怎么最稳
团队分支上,最稳的是 git revert,它不改写历史,别人 git pull 不会冲突。只有在确认是你自己独享的分支 时,才考虑 reset + 强制推送:
bash
git push --force-with-lease # 比 --force 安全:别人有新提交时会拒绝,避免覆盖队友
公共主干(main / master)上永远优先 revert,不要强推。
七、速查表(建议收藏)
| 我想撤销什么 | 命令 | 会丢代码吗 |
|---|---|---|
| 工作区改动 | git restore 文件 |
会(未保存的编辑) |
| 暂存(unstage) | git restore --staged 文件 |
不会 |
| 改最后 commit | git commit --amend |
不会 |
| 撤销 commit 留改动 | git reset --soft HEAD~1 |
不会 |
| 撤销 commit 删改动 | git reset --hard HEAD~1 |
会 |
| 回退已 push 的历史 | git revert 哈希 |
不会 |
小结
撤销翻车几乎都是因为没分清"改动在哪个区"+"有没有 push"。没 push 用 reset,已 push 用 revert;要留代码用 --soft,别手滑 --hard。 建议把第七节的速查表收藏,下次手抖前先看一眼。
评论区说说你被哪条命令坑过,我帮你分析当时该用哪个。