---
url: https://eoht.net/fe/git/reset.md
description: >-
  区分 Git reset 的 soft、mixed、hard 模式与 revert 撤销提交，说明回退前备份、冲突处理、reflog
  恢复以及已共享分支的推送边界。
---

# Git 版本回退：reset、revert 与恢复方法 {#git-history-rollback-guide}

先判断提交是否已推送并被他人使用。仅在本地整理未共享的提交时可以用 `reset`；撤销共享分支上的改动，通常使用新增撤销提交的 `revert`。

| 命令 | 分支历史 | 文件内容 |
| --- | --- | --- |
| `reset` | 将当前分支指向指定提交 | 是否改变暂存区和工作区取决于模式 |
| `revert` | 保留原提交，新增撤销提交 | 对指定提交的改动应用反向修改，并非完整恢复到某个历史快照 |

以下示例在目标仓库内执行。`main`、`abc1234` 和示例文件路径都要替换成实际值；不要把尖括号或中文括号当成命令参数。

## reset 的使用方法 {#git-reset-method}

### 查看版本，确定要回退的时刻 {#find-git-revision-to-reset}

```sh
git status --short --branch
git branch --show-current
git log --oneline --decorate -15
git diff
git diff --cached
```

先保存未提交和未跟踪文件，确认没有进行中的 merge、rebase 或 revert。下面的备份分支只保留当前提交，不保存未提交文件；如果已存在同名分支，换一个名字，不要覆盖它。

```sh
git branch backup-before-reset
git show --stat abc1234
```

### 回退操作 {#perform-git-reset}

三种模式任选一种。目标提交之后的提交会离开当前分支的主线，备份分支仍可保留它们。

| 模式 | 暂存区 | 工作区 | 常见用途 |
| --- | --- | --- | --- |
| `--soft` | 保持原样 | 保持原样 | 将本地提交重新整理为待提交改动 |
| `--mixed`（默认） | 重置到目标提交 | 保持原样 | 撤销提交和暂存，保留文件修改 |
| `--hard` | 重置到目标提交 | 重置到目标提交 | 明确丢弃本地改动，恢复目标内容 |

```sh
# 保留暂存和工作区改动
git reset --soft abc1234
```

```sh
# 保留工作区改动，重新选择需要暂存的文件
git reset --mixed abc1234
```

```sh
# 仅在确认已备份、确需丢弃改动时执行
git reset --hard abc1234
```

`--hard` 会覆盖受影响的已跟踪文件；妨碍写入目标文件的未跟踪路径也可能被删除。执行后用 `git status`、`git log -5 --oneline` 和项目自身测试确认结果。

### 恢复回退前的提交 {#recover-state-before-git-reset}

优先查看 `backup-before-reset`。没有备份分支时，可从本地 reflog 找到 reset 前的提交：

```sh
git reflog --date=local
```

核对旧提交 SHA 后，先建立恢复分支，避免再一次覆盖当前状态。下面仍需将 `abc1234` 换为实际找到的旧提交。

```sh
git branch recovered-before-reset abc1234
git show --stat recovered-before-reset
```

reflog 是当前仓库的本地记录，会过期；已被回收的对象，以及从未提交或暂存过的文件内容，不能指望它恢复。切换到恢复分支前仍需保存当前工作区。

### 确需改写远端历史时的处理 {#force-push-reset-history}

未推送的本地提交回退无需强推。共享分支优先使用下方 `revert`；只有仓库维护者和协作者已确认改写范围、备份可恢复且分支规则允许时，才考虑强制更新。

在改写前先获取远端状态，确认当前历史包含远端已有提交，再记录即将替换的完整 SHA。下面以 `main` 为例，后续命令在同一终端执行：

```sh
git fetch origin main
git log --oneline --left-right HEAD...origin/main
reset_old_main=$(git rev-parse refs/remotes/origin/main)
```

如果输出显示尚未处理的远端提交，先停止并协调。完成约定的本地回退及验证后，只更新目标分支：

```sh
test -n "${reset_old_main:-}" &&
  git push --force-with-lease="refs/heads/main:$reset_old_main" origin HEAD:refs/heads/main
```

远端 SHA 已改变时该命令会拒绝推送，应重新核对新增提交；不要改用 `--force` 绕过拒绝。推送后对照 `git rev-parse HEAD` 与 `git ls-remote origin refs/heads/main`。

## revert 的使用方法 {#git-revert-method}

先确保工作区、暂存区干净，再查出**要撤销的那一个提交**。它不是“希望回到的版本”；撤销合并提交还涉及主线父提交，应另外核对，不能直接套用普通提交示例。

```sh
git status --short
git log --oneline -15
git show abc1234
git revert --no-commit abc1234
```

`--no-commit` 只将反向改动放入工作区和暂存区。成功后检查差异、运行项目验证，再明确创建撤销提交：

```sh
git diff --cached
git diff --check
git commit -m "Revert the changes from abc1234"
```

若出现冲突，先用 `git status` 找到冲突文件，手工解决并用 `git add -- 实际文件路径` 暂存，再依照 Git 提示使用 `git revert --continue`；结束后检查 `git log -1` 和暂存区，若 `--no-commit` 留下的改动尚未提交，再执行上述检查与 `git commit`。决定取消正在进行的撤销时使用 `git revert --abort`。

### 提交到远程 {#push-revert-commit}

确认撤销提交和验证结果正确后，按仓库的评审流程提交；允许直接推送的 `main` 分支可执行普通推送：

```sh
git push origin HEAD:main
```

若远端有新提交导致拒绝，先同步并重新核对结果，不需要强推。

参考：[git reset](https://git-scm.com/docs/git-reset)、[git revert](https://git-scm.com/docs/git-revert)、[git push 的 lease 保护](https://git-scm.com/docs/git-push)。

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

如果目标是整理多个提交而非单纯回退，先查看 [Git 合并与精简提交](/fe/git/commit)；需要重建分支提交记录时，继续阅读 [Git 重置提交记录](/fe/git/reset-commit)。
