搞了这么多年开发,Git翻车现场我见得太多了。深夜上线前手一抖把release分支 reset --hard 掉了,新同事把老代码覆盖提交,发布之后发现提交里带了不该带的大文件……这些场景但凡遇上一次,你就能体会到"撤销"不是锦上添花,是保命功能。这也是我把《Git 撤销与急救篇》单独拿出来写的原因——Git 命令那么多,真正让人焦虑的永远不是某个功能不会用,而是改错了、写错了、提交错了之后,怎么把代码"救"回来。
这篇是系列教程的第八篇,前几篇已经覆盖了 Git 安装配置、常用命令、分支管理和冲突解决,所以这篇我直接进入正题:代码产生后果之后如何撤销和回滚。无论你是刚入门的开发者,还是在团队里带人的老手,这篇文章整理的撤销手段、回滚策略和避坑经验,日常开发基本都够用。
1. 先搞明白:你到底在撤销哪个"区"
Git 让人头大的根本原因,是同一份代码同时存在多个副本:你磁盘上的工作区、执行 add 之后的暂存区、commit 后的本地仓库、push 后的远程仓库。不同区域的撤销方式区别非常大,命令也完全不一样。如果你连自己的改动在哪一层都没搞清楚,再牛的撤销命令也救不了你。
1.1 四个存储区域,对应四种"后悔"场景
用最直白的方式理解:
- 工作区(Working Tree):你本地能直接看到的目录和文件,你打开编辑器改的就是它。
- 暂存区(Index/Stage):执行
git add之后代码进入的地方。它像一个"候车区",等确认完统一提交。 - 本地仓库(Local Repository):执行
git commit后的代码所在位置,存在项目下的.git目录里,只有你本机可见。 - 远程仓库(Remote Repository):执行
git push后代码进入的地方,在 GitHub、GitLab、Gitee 或公司自建的服务器上,团队共享。
撤销的本质,就是拿某一个区域的内容去覆盖另一个区域的内容。所以动手之前必须连续回答三个问题:我要动哪个区域?我想用哪个区域来覆盖它?被覆盖的内容以后还有没有用?
这就好比手机短信的草稿箱:打了一半的文字还没发送(工作区)、点了保存但还没发出去(暂存区)、已经发出去的聊天记录(远程仓库)。这三层内容的"撤回"方式完全不同,代价也不一样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
1.2 HEAD、Index、Working Tree 三个核心概念
Git 官方文档和网上大量教程会反复出现三个词:HEAD、index、working tree。它们分别对应:
| 概念 | 含义 | 可以粗暴理解为 |
|---|---|---|
| HEAD | 指向当前分支最近一次提交的指针 | 本地仓库的"当前状态" |
| Index | 暂存区 | 准备提交的候选内容 |
| Working Tree | 磁盘上实际存在的文件 | 你编辑器里看到的内容 |
几乎所有撤销命令都是在调整这三个对象之间的关系。比如 git reset --hard 是把 HEAD、index、working tree 三者全部强制同步到某个历史提交;git restore <file> 只是把 working tree 里的一个文件用 index 里的版本覆盖;git reset --soft 则只移动 HEAD,暂存区和工作区一概不动。
理解到这个程度就够用了。接下来我按照"从轻到重"的顺序,把各个场景的撤销命令依次过一遍。
2. 提交前的急救:工作区与暂存区的撤销操作
提交之前最容易翻车,也最容易救。很多新手一慌就搜网上的命令,结果把没提交的改动直接弄没了。实际上这个阶段的撤销手段非常温和,基本不会造成不可逆损失。
2.1 文件改了但还没 add:如何撤销工作区修改
场景:你用编辑器改了一个文件,改到一半觉得不对劲,想回到上一次提交时的状态。
bash复制# 传统写法
git checkout -- src/App.js
# 新版推荐写法
git restore src/App.js
两条命令效果一样:把工作区指定文件恢复成 index(暂存区)里的版本。如果你改完文件后还没有执行 git add,暂存区里装的就是上一次 commit 的内容,所以这个操作就会把文件回退到最近一次提交的状态。
需要注意几点:
git restore <file>是 Git 2.23 之后引入的现代命令,语义更清晰,不用像checkout那样靠--分隔符来区分文件和分支,我建议新项目直接用它。- 如果你想撤销整个目录的文件,把文件名换成目录路径即可:
git restore src/。 - 如果你想把所有工作区改动都扔掉,在项目根目录执行
git restore .,注意最后的点代表当前目录。
警告:这个操作不可逆。工作区里未提交的改动会直接被覆盖,没有任何确认提示。执行之前务必确认这些改动你真的不要了。如果拿不准,先把文件复制到别处,或者用
git stash临时保存。
2.2 误 add 了怎么办:从暂存区撤下
场景:手滑执行了 git add .,把不该提交的文件也加进去了。此时文件还在暂存区,但还没有 commit,你想把某个文件从暂存区撤下来,但保留工作区的实际修改。
bash复制# 传统写法
git reset HEAD src/App.js
# 新版推荐写法
git restore --staged src/App.js
执行后,src/App.js 会从暂存区移出,回到"已修改但未暂存"的状态,你在编辑器里的改动不会丢。
另一个高频操作是把所有文件从暂存区撤下:
bash复制git reset
不带任何参数时,git reset 默认是 `-
