PC TOOLS

EP07. “改错了怎么办:restore、reset、revert,以及不小心 commit 了密码”

首页 › PC 工具 › Developer Tools · Git & GitHub · EP07
约 11 分钟· #EP07#Git & GitHub
🔒 登录后可标记已读
  • 用 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 resetgit 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

官方文档:

  1. Pro Git 2.4 Undoing Things - git-scm.com
  2. Pro Git 7.7 Reset Demystified - git-scm.com
  3. Pro Git 7.3 Stashing and Cleaning - git-scm.com
  4. git-restore Documentation - git-scm.com
  5. git-revert Documentation - git-scm.com
  6. Removing sensitive data from a repository - GitHub Docs
  7. About push protection - GitHub Docs
📝 测验一下共 7 题