深入理解 git reset 的 soft、mixed、hard、keep 四种模式,掌握合并撤销的正确姿势,不再害怕误操作。
谁都会犯错:提交了不该提交的文件、合并错了分支、写完代码发现方向不对……
git reset 就是 Git 的”后悔药”,它能把你的仓库状态回退到任意历史提交。但不同模式的 reset 影响范围差别很大,搞错了可能丢代码!
一、
先理解三个区域
在学 reset 之前,必须搞清 Git 的三个工作区域:
1 2 3 4 5 6 7
| ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 仓库 │ │ 暂存区 │ │ 工作区 │ │ (Repo) │ → │ (Stage) │ → │ (Work) │ │ │ │ │ │ │ │ 提交历史 │ │ add 的文件 │ │ 你正在 │ │ │ │ │ │ 编辑的文件 │ └──────────┘ └──────────┘ └──────────┘
|
| 区域 | 说明 | 对应操作 |
|---|
| 仓库(Repository) | 已提交的历史记录 | git commit |
| 暂存区(Staging) | 等待提交的修改 | git add |
| 工作区(Working) | 你正在编辑的文件 | 直接用编辑器修改 |
记住
git reset 本质上是移动 HEAD 指针(当前所在的提交),不同模式决定了指针移动后,暂存区和工作区怎么处理。
二、
四种模式核心对比
| 模式 | 命令 | HEAD | 暂存区 | 工作区 | 使用场景 |
|---|
| soft | git reset --soft | 回退 | 保留 | 保留 | 只想修改提交信息 |
| mixed | git reset --mixed | 回退 | 清除 | 保留 | 撤销 add(默认模式) |
| hard | git reset --hard | 回退 | 清除 | 清除 | 完全放弃,重头再来 |
| keep | git reset --keep | 回退 | 尝试保留 | 尝试保留 | 安全回退,有冲突则中止 |

--hard 是唯一会丢失工作区修改的模式,执行前请三思!
三、
软重置 — soft
效果:只把 HEAD 指针回退一个提交,暂存区和工作区完全不动。
典型场景:修改上一次提交的信息
1 2 3 4 5 6 7 8
| git commit -m "feat: 添加用户登陆功能" ← "登陆"写错了
git reset --soft HEAD~1
git commit -m "feat: 添加用户登录功能" ← 改正了
|
更简单的方式
如果只是想改提交信息,不改代码,用 --amend 更方便:
1
| git commit --amend -m "feat: 添加用户登录功能"
|
软重置的状态变化
1 2 3 4 5 6 7 8 9 10 11
| # 重置前 仓库: A → B → C (HEAD) 暂存区: [C 的修改] 工作区: [C 的代码]
# 软重置到 B git reset --soft HEAD~1
仓库: A → B (HEAD) 暂存区: [C 的修改] ← 还在! 工作区: [C 的代码] ← 还在!
|
你的代码还在暂存区,可以重新 commit。
四、
混合重置 — mixed(默认)
1 2 3
| git reset --mixed HEAD~1
git reset HEAD~1
|
效果:HEAD 回退,暂存区清除,工作区保留。
典型场景:撤销 git add
1 2 3 4 5 6 7 8 9 10
| git add .
git reset
git add src/
|
mixed 的状态变化
1 2 3 4 5 6 7 8 9 10 11
| # 重置前 仓库: A → B → C (HEAD) 暂存区: [C 的修改] 工作区: [C 的代码]
# 混合重置到 B git reset HEAD~1
仓库: A → B (HEAD) 暂存区: [] ← 清空了! 工作区: [C 的代码] ← 还在!
|
你的代码还在,只是从暂存区移出来了,需要重新 git add。
最常用
mixed 是最常用的模式(也是默认模式),适用于大部分”想撤销但保留代码”的场景。
五、
硬重置 — hard
效果:HEAD 回退,暂存区和工作区全部清除,回到目标提交的状态。
典型场景:彻底放弃当前修改
1 2 3 4 5
| git reset --hard HEAD~1
git reset --hard abc1234
|
hard 的状态变化
1 2 3 4 5 6 7 8 9 10 11
| # 重置前 仓库: A → B → C (HEAD) 暂存区: [C 的修改] 工作区: [C 的代码]
# 硬重置到 B git reset --hard HEAD~1
仓库: A → B (HEAD) 暂存区: [] ← 清空了! 工作区: [B 的代码] ← C 的代码消失了!
|
hard 是危险操作!
- 工作区未提交的修改会被直接覆盖
- 被 reset 掉的提交如果没 push,很难找回
- 执行前务必确认你真的要丢弃这些代码
安全措施:执行前可以先 git stash 备份:
1 2 3
| git stash git reset --hard HEAD~1 git stash pop
|
六、
保留重置 — keep
效果:类似 mixed,但会检查冲突。如果工作区的修改和目标提交有冲突,会直接中止,不会丢代码。
典型场景:安全回退
1 2
| git reset --keep HEAD~1
|
keep vs mixed 的区别
| 场景 | mixed | keep |
|---|
| 工作区有修改,目标提交没冲突 | 成功 | 成功 |
| 工作区有修改,目标提交有冲突 | 覆盖你的修改 | 报错中止,保留修改 |
推荐
如果你不确定用哪个,--keep 比 --mixed 更安全。它会保护你的工作区修改不被意外覆盖。
七、
合并后撤销
这是新手最常遇到的问题:合并了错误的分支,怎么撤销?
首先判断:合并后有没有推送到远程?
1 2 3 4 5 6 7
| ┌─────────────────────┬─────────────────────────────┐ │ 还没 push │ 已经 push │ ├─────────────────────┼─────────────────────────────┤ │ 安全,用 reset 撤销 │ 需要 revert(生成新提交) │ │ git reset --hard │ git revert -m 1 <commit> │ │ 本地操作,不影响别人 │ 安全撤销,不会改写历史 │ └─────────────────────┴─────────────────────────────┘
|
情况 1:还没推送 — 用 reset
1 2 3 4 5 6 7 8 9 10 11 12 13
| git log --oneline
git reset --hard HEAD~1
git reset --hard e4f5g6h
|
情况 2:已经推送 — 用 revert
1 2 3 4 5
| git revert -m 1 <merge_commit_hash>
git push
|
为什么已推送的不能用
reset? 因为 reset 会改写历史。如果别人已经 pull 了你的合并提交,你 reset 后 force push,别人的分支就会和远程不一致,造成混乱。revert 不会改写历史,而是生成一个新提交来”抵消”合并的改动。
八、
IDEA 中的对应操作
IntelliJ IDEA / WebStorm 的 Git 面板中,右键提交记录可以看到以下菜单:
| IDEA 菜单项 | 等价 Git 命令 | 说明 |
|---|
| 撤销提交(Undo Commit) | git reset --soft HEAD~1 | 保留修改到暂存区,可以重新提交 |
| 删除提交(Delete Commit) | git reset --keep HEAD~1 | 删除提交,保留不冲突的文件修改 |
| 回退到此提交 | git reset --hard <commit> | 危险!完全回退到指定提交 |
| 压缩提交 | git rebase -i(squash) | 合并多个提交(属于变基操作) |
| 修改提交信息 | git rebase -i(reword) | 修改提交描述 |
| 从这里执行交互式变基 | git rebase -i <commit> | 完整变基操作 |
小技巧:
- 撤销提交最常用:写完代码提交后发现信息不对,点”撤销提交”就能修改后重新提交
- 删除提交也很实用:想删掉一个提交但保留代码改动
九、
找回丢失的提交:reflog
如果你不小心用 git reset --hard 丢掉了提交,别慌!Git 有一个”后悔药的后悔药”——reflog。
什么是 reflog?
reflog 记录了 HEAD 指针的每一次移动,包括那些被 reset 掉的提交:
输出示例:
1 2 3
| a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1 e4f5g6h HEAD@{1}: commit: feat: 这个提交被 reset 掉了 i7j8k9l HEAD@{2}: commit: feat: 之前的功能
|
找回被删除的提交
1 2 3 4 5 6 7 8 9
| git reflog
git reset --hard e4f5g6h
git branch rescue e4f5g6h git checkout rescue
|
reflog 的有效期:默认保留 90 天的记录。超过 90 天的 reflog 条目会被 Git 自动清理。所以发现误操作要尽快恢复。
总结:reset 选择指南
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 想修改上一次提交的信息? └── git reset --soft HEAD~1 或 git commit --amend
想撤销 git add,保留代码? └── git reset(默认 mixed)
想完全放弃当前修改? └── git reset --hard HEAD~1 ⚠️ 危险
想安全回退,有冲突就中止? └── git reset --keep HEAD~1 ✅ 推荐
不小心 hard reset 丢了代码? └── git reflog → git reset --hard <hash>
|
速查表
| 命令 | HEAD | 暂存区 | 工作区 | 场景 |
|---|
git reset --soft HEAD~1 | 回退 | 保留 | 保留 | 改提交信息 |
git reset HEAD~1 | 回退 | 清除 | 保留 | 撤销 add |
git reset --hard HEAD~1 | 回退 | 清除 | 清除 | 完全放弃 |
git reset --keep HEAD~1 | 回退 | 安全保留 | 安全保留 | 安全回退 |
git reflog | — | — | — | 找回丢失提交 |