---
url: https://eoht.net/fe/git/reset-commit.md
description: >-
  区分 Git 凭据泄漏处置与普通提交历史重置：泄漏后先撤销或轮换凭据，再按需清理历史；普通重置则先备份、核对文件快照，并使用带远端提交校验的
  force-with-lease 推送。
---

# Git 重置提交记录 恢复历史 清理提交记录 {#reset-git-commit-history}

**处理密码泄漏**与**整理提交历史**是两件不同的事。删除当前文件或重写分支，既不会让已经泄漏的凭据失效，也不能保证其他副本中的内容消失。

## 密码或令牌泄漏：先让凭据失效 {#revoke-leaked-credentials}

1. **立即撤销或轮换泄漏的密码、API Key、访问令牌等凭据**，更新使用它们的应用与部署配置，并检查异常访问记录。不要等清理完 Git 历史才处理凭据。
2. 删除代码中的秘密，改用环境变量或秘密管理工具；检查提交内容，避免把新凭据再次提交。
3. 按数据敏感程度判断是否还需要清理历史。需要时，先协调协作者暂停相关分支更新，再按 [GitHub 敏感数据清理指南](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository) 使用 `git-filter-repo` 处理受影响的文件、历史路径和引用。
4. 清理后复核所有受影响的分支与标签，协调协作者处理旧克隆，避免旧历史被再次推回。涉及 GitHub 的 PR 引用或缓存页面时，按官方指南联系 GitHub Support；是否能协助移除取决于具体风险。

::: warning 强推不是彻底删除
旧提交仍可能存在于 fork、他人的克隆、PR 引用及缓存中。你无法通过一次推送删除所有人的副本，也不能因此认定秘密从未泄漏。不要为“方便回滚”再创建或上传含秘密的备份分支、标签或压缩包。
:::

## 普通历史重置：只保留当前文件快照 {#reset-history-with-current-snapshot}

如果只是希望从一个新的初始提交开始，可以创建孤儿分支。它与普通新分支不同：首次提交没有父提交，但这并不会清除其他分支、标签或远端缓存中的旧历史。

下面的示例**仅适用于没有敏感数据清理需求、确定要完整保留当前已跟踪文件的普通重置**。执行前须获得仓库维护者同意，与协作者约定暂停更新；确认没有进行中的合并或变基，并将需要保留的旧历史备份到受控位置、验证可恢复。若分支保护不允许强推，停止并走仓库审批流程，不要绕过保护。

### 1. 核对工作区与远端 {#check-history-reset-state}

先确认当前分支是 `main`，`git status --short` 没有输出，再获取远端状态：

```sh
git branch --show-current
git status --short
git fetch origin
git rev-parse HEAD
git rev-parse refs/remotes/origin/main
```

核对 `origin` 指向的仓库正确，且最后两个 SHA 完全一致；如果有未提交改动、未跟踪文件或本地与远端不一致，先停止处理，不要直接执行后续命令。

### 2. 创建并检查新快照 {#create-reviewed-root-commit}

在同一个终端会话中保存**已与远端核对一致的本地 `main` SHA**，并从 `main` 创建尚不存在的孤儿分支：

```sh
history_expected_main="$(git rev-parse refs/heads/main)"
git checkout --orphan clean-history main
git diff --cached --stat
git diff --cached
```

`checkout --orphan` 会按起点准备工作区和暂存区，当前已跟踪文件已经在暂存区中，因此这里不需要 `git add -A`。逐项检查文件，确认没有秘密、临时文件或不应公开的内容；如果需要增删文件，按明确路径单独暂存并重新检查，不要盲目收录整个目录。

确认后提交，再检查只有一个提交、文件快照没有意外变化：

```sh
git commit -m "chore: start a new history"
git rev-list --count HEAD
git diff --exit-code "$history_expected_main" HEAD --
```

此示例完整保留原快照，因此提交数量应为 `1`，最后一条命令应无差异且退出码为 `0`。任一检查不符合预期，都不要推送。

### 3. 只更新指定的远端分支 {#push-reviewed-history-with-lease}

再次确认获准覆盖远端 `main` 后，使用带明确预期 SHA 的保护推送：

```sh
git push --force-with-lease="refs/heads/main:$history_expected_main" origin HEAD:refs/heads/main
```

只有远端 `main` 仍指向此前核对的提交，这次更新才会成功。如果被拒绝，先查明远端是否新增了提交或存在分支保护；不要改成 `--force`，也不要刷新预期 SHA 后直接重试。

完成后核对远端提交与新快照，并通知协作者按约定重新克隆或迁移未合并的工作。旧的本地 `main` 与备份仍保留原历史，不能再把它们直接合并或推回新的 `main`。确认新历史可用之前，不要删除这些普通重置场景下的恢复点。

具体语义见 [Git checkout 孤儿分支文档](https://git-scm.com/docs/git-checkout) 和 [Git push 的 force-with-lease 文档](https://git-scm.com/docs/git-push)。

## Git 学习路径 {#git-learning-path}

重写提交前，先了解 [Git 版本回退](/fe/git/reset) 中 `reset` 与 `revert` 的区别；如果目标是移除大文件并缩小仓库，继续阅读 [Git 缩减仓库](/fe/git/clean-up)。
