EP07. “改错了怎么办:restore、reset、revert,以及不小心 commit 了密码”
🔒 登录后可标记已读- 用 Git 最大的安心感就是「什么都救得回来」,但前提是用对指令——
reset --hard用错了,没 commit 的改动真的会消失 - 选哪个指令,只看两个问题:改动 commit 了没有?commit 推上 GitHub 了没有?
- 已经 push 出去的 commit,要用 revert(新增一个反向 commit),不要用 reset 改写历史
- 不小心把 API Key、数据库密码 commit 上 GitHub:第一件事是去撤销/更换那个密码,删历史是第二步
重点内容
先判断:我在哪个阶段?
flowchart TD
A{"改动 commit 了吗?"} -->|还没| B{"add 了吗?"}
B -->|还没| C["git restore 文件<br/>丢弃改动"]
B -->|add 了| D["git restore --staged 文件<br/>移出暂存区"]
A -->|commit 了| E{"push 了吗?"}
E -->|还没| F["git commit --amend<br/>或 git reset"]
E -->|push 了| G["git revert<br/>新增反向 commit"]
还没 commit:git restore
| 情况 | 指令 | 结果 |
|---|---|---|
| 文件改坏了,想回到上次 commit 的样子 | git restore index.html | 丢弃工作区的改动(⚠️ 救不回来) |
git add 错了文件,想移出暂存区 | git restore --staged .env | 移出暂存区,文件改动还在 |
| 想拿某个旧 commit 里的版本 | git restore --source a1b2c3d index.html | 把文件内容换成那个 commit 的版本 |
📌 旧教程常写 git checkout -- 文件 和 git reset HEAD 文件,作用分别等于 git restore 文件 和 git restore --staged 文件。git restore 从 Git 2.23 开始有,意思更直白。
刚 commit、还没 push:amend 或 reset
说明写错、漏加一个文件 → --amend:
git add 漏掉的文件.css
git commit --amend -m "修正首页页脚的联络电话"
--amend 会用新的 commit 取代最后一个 commit。
想撤掉最近的 commit → git reset:
| 指令 | commit | 暂存区 | 工作区的文件 |
|---|---|---|---|
git reset --soft HEAD~1 | 撤掉 | 改动保留在暂存区 | 不变 |
git reset HEAD~1(默认 --mixed) | 撤掉 | 清空 | 改动还在,变回未暂存 |
git reset --hard HEAD~1 | 撤掉 | 清空 | ⚠️ 改动也一起删掉 |
HEAD~1= 最新 commit 的前一个;HEAD~3= 往回三个- 最安全的是
--soft或默认的--mixed,改动都还在,只是「撤掉存档这个动作」
已经 push 了:git revert
git log --oneline # 找到要撤销的 commit,例如 9f8e7d6
git revert 9f8e7d6 # 产生一个新 commit,内容刚好是反向改动
git push
git reset | git revert | |
|---|---|---|
| 做法 | 把分支指针往回移,改写历史 | 新增一个 commit 抵消旧改动,历史不变 |
| 适合 | 只在自己本地、还没 push 的 commit | 已经 push、别人可能已经 pull 的 commit |
| 对协作者的影响 | push 时要 --force,会打乱别人的历史 | 没影响,大家照常 pull |
救命绳:git reflog
reset --hard 之后发现删错了?只要那些改动曾经 commit 过,通常都救得回来:
git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
9f8e7d6 HEAD@{1}: commit: 新增 WhatsApp 按钮
找到被删掉之前的那一行,回到那里:
git reset --hard 9f8e7d6
⚠️ 从来没 commit 过的改动,Git 没有记录,reflog 也救不了——这就是「常 commit」的原因。
暂时收起改动:git stash
改到一半,突然要切去别的分支修急事:
git stash # 把未 commit 的改动收起来,工作区变干净
git switch main # 去修急事……
git switch new-header
git stash pop # 把刚才的改动拿回来
不小心 commit 了密码或 API Key
flowchart LR
subgraph S1["第 1 步:立刻作废"]
A1["到服务商后台撤销/更换<br/>API Key、密码、Token"]
end
subgraph S2["第 2 步:移出追踪"]
B1["加进 .gitignore<br/>git rm --cached 文件"]
end
subgraph S3["第 3 步:视情况清历史"]
C1["git-filter-repo<br/>或联络 GitHub Support"]
end
S1 --> S2 --> S3
- 最重要是第 1 步:一旦推上 GitHub(尤其是 Public 仓库),就要当作已经外泄——自动扫描机器人几分钟内就可能抓到。换掉密码之后,旧密码留在历史里也没用了
- 第 2 步只能防止之后的 commit 再带到这个文件,旧 commit 里仍然有
- 第 3 步改写整个历史,所有 commit hash 都会变、协作者要重新 clone、别人已经 Fork 的副本也删不掉——GitHub 官方文档也提醒先评估是否真的必要,工具推荐
git-filter-repo - 预防:仓库 Settings → Advanced Security(或 Code security) 开启 Push protection,push 含密钥的 commit 时会被直接挡下来
学完你会
- ✅ 依「commit 了没?push 了没?」选对撤销指令
- ✅ 分得清 reset 三种模式、reset 与 revert 的差别
- ✅ 用 reflog 救回误删的 commit、用 stash 暂存改动
- ✅ 知道密码外泄时的正确处理顺序
常见错误
- ❌ 不清楚状况就
git reset --hard:没 commit 的改动直接消失,而且救不回来。 - ❌ 对已经 push 的 commit 用 reset 再
--force:同事的历史被打乱,要用 revert。 - ❌ 以为删掉含密码的文件再 commit 就安全了:旧 commit 里还看得到,第一步要换密码。
- ❌ 对已经 push 的 commit 用
--amend:一样是改写历史,会跟远端分歧。 - ❌ 以为
git restore 文件可以复原:它会丢弃未 commit 的改动,这个动作本身救不回来。
Sources
官方文档:
- Pro Git 2.4 Undoing Things - git-scm.com
- Pro Git 7.7 Reset Demystified - git-scm.com
- Pro Git 7.3 Stashing and Cleaning - git-scm.com
- git-restore Documentation - git-scm.com
- git-revert Documentation - git-scm.com
- Removing sensitive data from a repository - GitHub Docs
- About push protection - GitHub Docs