强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Git 完全指南 / 08 - 撤销操作:reset、revert、checkout、restore

第八章:撤销操作

犯错是开发的常态,Git 提供了丰富的撤销工具,让你从容应对各种情况。


8.1 撤销操作全景图

┌──────────┬──────────────┬──────────────┬──────────────┐
│  操作目标 │   工作区      │   暂存区      │   仓库       │
├──────────┼──────────────┼──────────────┼──────────────┤
│ 丢弃修改 │ git restore  │ git restore  │ git reset    │
│          │              │ --staged     │ --soft HEAD~ │
│ 还原提交 │ git revert   │              │ git revert   │
│ 恢复文件 │ git checkout │ git reset    │ git checkout │
│          │ -- <file>    │ HEAD <file>  │ <commit>     │
└──────────┴──────────────┴──────────────┴──────────────┘

8.2 git reset — 重置提交

git reset 移动 HEAD 指针,可选择性地修改暂存区和工作区。

8.2.1 三种模式

模式HEAD暂存区工作区用途
--soft✅ 移动❌ 不变❌ 不变撤销提交,保留所有修改在暂存区
--mixed (默认)✅ 移动✅ 重置❌ 不变撤销提交和暂存,保留工作区修改
--hard✅ 移动✅ 重置✅ 重置完全重置,丢弃所有修改

8.2.2 使用示例

# 撤销最后一次提交,保留修改在暂存区
$ git reset --soft HEAD~1

# 撤销最后一次提交,取消暂存(默认 --mixed)
$ git reset HEAD~1

# 完全撤销最后一次提交(危险:丢失修改)
$ git reset --hard HEAD~1

# 撤销最后 3 次提交
$ git reset --soft HEAD~3

# 重置到特定提交
$ git reset --soft abc1234

# 取消暂存文件(保留工作区修改)
$ git reset HEAD file.txt
# 或 Git 2.23+
$ git restore --staged file.txt

# 丢弃工作区修改(恢复到暂存区版本)
$ git checkout -- file.txt
# 或 Git 2.23+
$ git restore file.txt

8.2.3 reset 流程图

初始状态:
HEAD → C3 → [暂存区] → [工作区]

git reset --soft HEAD~1:
HEAD → C2 → [暂存区=C3] → [工作区=C3]
提交被撤销,但修改仍在暂存区,可以直接重新提交

git reset --mixed HEAD~1 (默认):
HEAD → C2 → [暂存区=C2] → [工作区=C3]
提交和暂存被撤销,修改仍在工作区

git reset --hard HEAD~1:
HEAD → C2 → [暂存区=C2] → [工作区=C2]
一切回到 C2 的状态,C3 的修改完全丢失

8.3 git revert — 撤销提交(安全方式)

git revert 创建一个新提交来反转指定提交的变更,不修改历史。

# 撤销指定提交
$ git revert abc1234

# 撤销多个提交
$ git revert abc1234 def5678

# 撤销提交范围
$ git revert abc1234..def5678

# 撤销合并提交(指定保留哪个父提交)
$ git revert -m 1 <merge-commit>

# 不自动提交(先审查)
$ git revert --no-commit abc1234

# 撤销 merge commit 的示例
$ git revert -m 1 HEAD    # 撤销最后一次合并

revert vs reset 对比

特性git revertgit reset
修改历史❌ 创建新提交✅ 重写历史
安全性🟢 高🔴 低(公共分支危险)
适用场景公共分支个人分支
提交记录保留撤销记录丢失被撤销的提交
可追溯性

⚠️ 规则:已经推送到远程的提交使用 revert,未推送的可以使用 reset


8.4 git checkout / git restore — 恢复文件

Git 2.23 引入了 git restoregit switch 来替代 git checkout 的部分功能。

8.4.1 丢弃工作区修改

# 传统方式
$ git checkout -- file.txt

# 新方式(推荐)
$ git restore file.txt

# 恢复多个文件
$ git restore file1.txt file2.txt

# 恢复整个目录
$ git restore src/

# 恢复所有文件
$ git restore .

8.4.2 取消暂存

# 传统方式
$ git reset HEAD file.txt

# 新方式(推荐)
$ git restore --staged file.txt

# 取消暂存多个文件
$ git restore --staged file1.txt file2.txt

# 取消暂存所有文件
$ git restore --staged .

8.4.3 从特定提交恢复文件

# 从上一个提交恢复文件
$ git restore --source=HEAD~1 file.txt

# 从特定提交恢复
$ git restore --source=abc1234 file.txt

# 从其他分支恢复
$ git restore --source=feature file.txt

# 传统方式
$ git checkout abc1234 -- file.txt
$ git checkout feature -- file.txt

8.5 git clean — 清理未追踪文件

# 预览要删除的文件(干运行)
$ git clean -n
$ git clean --dry-run

# 删除未追踪文件
$ git clean -f

# 删除未追踪文件和目录
$ git clean -fd

# 包含被忽略的文件
$ git clean -fdx

# 交互式清理
$ git clean -i
参数说明
-n预览模式,不实际删除
-f强制删除
-d包含目录
-x包含被 .gitignore 忽略的文件
-X只删除被忽略的文件
-i交互式选择

8.6 修改最近一次提交

# 修改提交信息
$ git commit --amend -m "New commit message"

# 追加文件到上次提交(不修改信息)
$ git add forgotten-file.txt
$ git commit --amend --no-edit

# 修改作者信息
$ git commit --amend --author="New Name <new@email.com>"

8.7 恢复丢失的提交

使用 reflog 恢复

# 1. 查看 reflog 找到丢失的提交
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~3
def5678 HEAD@{1}: commit: This commit was lost!

# 2. 恢复
$ git cherry-pick def5678
# 或
$ git reset --hard def5678

使用 fsck 恢复

# 查找悬空对象
$ git fsck --unreachable --no-reflogs

# 查看悬空提交
$ git show <dangling-commit-hash>

# 恢复
$ git branch recovered <dangling-commit-hash>

8.8 撤销操作速查表

场景命令
丢弃工作区修改git restore <file>
取消暂存git restore --staged <file>
修改上次提交git commit --amend
撤销本地提交(保留修改)git reset --soft HEAD~1
撤销本地提交(丢弃修改)git reset --hard HEAD~1
撤销已推送的提交git revert <commit>
清理未追踪文件git clean -fd
恢复误删文件git restore --source=HEAD <file>
恢复误删分支git branch <name> <reflog-hash>
恢复误 reset 的提交git reflog + git reset --hard HEAD@{n}

业务场景

场景推荐方案
改了不该改的文件git restore <file>
提交了不该提交的文件git restore --staged <file> + 更新 .gitignore
提交信息写错了git commit --amend
提交了错误代码(未推送)git reset --soft HEAD~1
需要撤销线上某个提交git revert <commit>
本地实验失败想回退git reset --hard HEAD
误删分支/提交git reflog 恢复

扩展阅读


🔗 上一章07 - 历史查看 | 下一章09 - 标签管理