撤销文件修改是 Git 中一个非常常见且重要的操作。根据文件当前所处的状态(未暂存、已暂存、已提交),撤销的方法不同。

下图清晰地展示了针对不同情况的撤销路径和对应命令:

flowchart TD A[工作目录<br>文件已修改] -->|git restore <file>| B[恢复至上一次暂存或提交的版本] C[暂存区<br>文件已暂存] -->|git restore --staged <file>| A C -->|git reset HEAD <file><br>(旧语法)| A D[本地仓库<br>文件已提交] -->|git revert <commit>| E[生成一个新提交来撤销更改] D -->|git reset --hard HEAD~1<br>(危险!)| F[彻底删除上次提交]

下面通过结合图示,对每种情况进行详细说明。

情况一:撤销工作区的修改(未添加到暂存区)

当修改了一个文件,但还没有执行 git add,此时若想放弃所有修改,恢复到上一次提交(或上一次暂存)时的样子。

命令:

git restore <文件名>
git restore . # 撤销所有未添加到暂存区的修改

或者(较旧的 Git 版本使用 checkout

git checkout -- <文件名>
git checkout -- . # 撤销所有未添加到暂存区的修改

示例:
修改了 index.html 文件,但改乱了,想重来。

# 查看当前状态,会看到 modified: index.html (红色)
git status

# 撤销对 index.html 的修改
git restore index.html

# 再次查看状态,工作区是干净的
git status

⚠️ 警告: 这个操作是不可逆的!所做的修改将被永久丢弃,无法通过 Git 找回。请谨慎使用。


情况二:撤销暂存区的修改(已 git add,但未 git commit

当已经将修改的文件添加到了暂存区,但现在想把它从暂存区“挪回”工作区,使其变回“未暂存”状态。这不会丢弃文件内容的修改。

命令:

git restore --staged <文件名>

或者(较旧的 Git 版本使用 reset

git reset HEAD <文件名>

示例:
执行了 git add index.html 后,发现这个文件还有问题,不应该在这次提交。

# 查看当前状态,会看到 Changes to be committed: modified: index.html (绿色)
git status

# 将 index.html 从暂存区移回工作区
git restore --staged index.html

# 再次查看状态,会看到 index.html 又变回了未暂存状态(红色)
git status

# 此时,如果想撤销工作区的修改,可以再执行情况一的操作
git restore index.html

情况三:撤销已提交的修改(已 git commit

这种情况更复杂一些,分为多种场景。

场景 3.1:撤销最新的提交,但保留修改内容(像没提交过一样)

撤销最后一次提交,但保留所有文件的修改,以便重新修改后再次提交。

命令:

git reset --soft HEAD~1
  • HEAD~1 表示上一个提交。
  • --soft 选项表示保留工作区和暂存区的所有修改。

示例:
刚完成了一次提交,但马上意识到漏了两个文件,或者提交信息写错了。

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

# 添加漏掉的文件,或做其他修改
git add missing-file.js

# 重新提交
git commit -m "正确的提交信息"

场景 3.2:撤销最新的提交,并丢弃所有修改(彻底丢弃该提交)

想彻底丢弃最近的一次提交以及所有相关的文件修改。这是一个危险操作!

命令:

git reset --hard HEAD~1
  • --hard 选项会强制将工作区、暂存区和仓库历史都回退到上一次提交的状态。

示例:
觉得刚才的提交完全是错误的,想让它彻底消失。

# 彻底回退到上一次提交的状态,所有修改丢失
git reset --hard HEAD~1

⚠️ 严重警告: 此操作会永久丢弃提交中的修改!请确保你100%确定不需要这些内容了。如果提交已经推送到远程仓库,后续推送会需要强制推送(git push --force),这会给团队协作带来麻烦。

场景 3.3:安全撤销历史中的某次提交(推荐用于团队协作)

如果想撤销一个已经推送到远程仓库的提交,使用 git reset 会破坏团队其他人的历史。此时应该使用 git revert

git revert 会创建一个新的提交,这个新提交的内容正好与想撤销的提交内容相反,从而“抵消”掉那次提交的更改。这是最安全的方式。

命令:

git revert <提交的哈希值>

示例:
想撤销哈希值为 abc123 的提交。

# 1. 查看日志,找到要撤销的提交哈希
git log --oneline
# 输出可能是:abc123 (HEAD -> main) 添加了新功能

# 2. 执行撤销
git revert abc123

# 3. Git 会打开编辑器,输入新提交的信息,保存退出即可。
# 4. 将这次 revert 操作推送到远程
git push origin main

总结表格

文件状态 想达到的目的 命令 风险
已修改,未暂存 丢弃工作区的修改 git restore <file> ,修改永久丢失
已暂存,未提交 将文件移出暂存区 git restore --staged <file> 低,仅改变状态,不丢修改
已提交 撤销提交,保留修改 git reset --soft HEAD~1 中,需谨慎操作
已提交 彻底丢弃提交和修改 git reset --hard HEAD~1 极高,数据永久丢失
已推送到远程 安全地撤销历史提交 git revert <commit-hash> 低,最安全,推荐

最佳实践建议:

  • 在执行任何 --hard 重置或 restore 工作区文件之前,先使用 git statusgit log 确认当前状态。
  • 如果可能,先创建一个新分支,再执行危险操作,作为备份。
  • 对已经推送到公共仓库的提交,始终优先使用 git revert