Git中文社区

撤销操作

在任何阶段,你都有可能想要撤销某些操作。Git 提供了多种撤销手段,但要注意:有些撤销操作是不可逆的,这是在 Git 中少有的会丢失工作成果的场景之一。

场景速查

我想要……命令
丢弃工作区中某个文件的修改git restore <文件>
把文件从暂存区撤回(保留修改)git restore --staged <文件>
修改最后一次提交git commit --amend
撤销最近的提交,保留修改git reset --soft HEAD~1
彻底丢弃最近的提交和修改git reset --hard HEAD~1
撤销一个已推送的提交git revert <提交>
找回"丢失"的提交git reflog

丢弃工作区的修改

你修改了一个文件,但发现改错了,想让它回到上一次提交时的样子:

git restore app.js

# 丢弃所有文件的修改
git restore .

在旧版本 Git 中等价的命令是:

git checkout -- app.js

警告

这会永久丢弃工作区中尚未提交的修改,无法通过 Git 找回。执行前请确认。

取消暂存

你用 git add 把文件放入了暂存区,现在想撤回(但保留文件内容的修改):

git restore --staged app.js

# 旧写法
git reset HEAD app.js

修改最后一次提交

提交后发现漏了文件,或者提交说明写错了:

# 补上遗漏的文件
git add forgotten.js
git commit --amend

# 只修改提交说明
git commit --amend -m "新的提交说明"

# 不修改提交说明
git commit --amend --no-edit

--amend 会用一个新的提交替换掉原来的最后一次提交。

警告

如果最后一次提交已经推送到远程,并且其他人可能已经基于它工作,就不要再 amend 了,否则会造成历史分叉。

git reset:移动分支指针

git reset 会把当前分支指向另一个提交,根据参数不同,对暂存区和工作区的影响也不同:

模式分支指针暂存区工作区适用场景
--soft移动保留保留想重新组织提交
--mixed(默认)移动重置保留撤销提交和暂存,保留修改
--hard移动重置重置彻底丢弃
# 撤销最近一次提交,修改仍在暂存区
git reset --soft HEAD~1

# 撤销最近一次提交,修改回到工作区(未暂存)
git reset HEAD~1

# 彻底回到上一个提交,丢弃之后的一切修改
git reset --hard HEAD~1

# 回退到指定提交
git reset --hard a1b2c3d

# 让本地分支和远程完全一致
git reset --hard origin/main

把多个提交合并成一个

# 把最近 3 个提交合并为一个
git reset --soft HEAD~3
git commit -m "完成登录模块"

git revert:用新提交撤销

reset 会改写历史,不适合用于已经推送到公共分支的提交。这时应该使用 git revert,它会创建一个新的提交来抵消指定提交的修改:

# 撤销某个提交
git revert a1b2c3d

# 撤销最近一次提交
git revert HEAD

# 撤销一个合并提交(-m 1 表示保留主线)
git revert -m 1 <合并提交>

提示

记住一条原则:本地未推送的提交用 reset,已推送的提交用 revert。

找回丢失的提交:git reflog

git reset --hard 之后后悔了?别慌。Git 会在 reflog 里记录 HEAD 的每一次移动:

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: 完成登录模块
# ...

# 回到被 reset 掉的那个提交
git reset --hard e4f5g6h

只要提交过的内容,在 reflog 过期(默认 90 天)之前几乎总能找回来。这也是"尽早提交、经常提交"的另一个好处。

清理未跟踪的文件

git clean 用于删除工作区中未被跟踪的文件:

# 先预览会删除什么
git clean -n

# 删除未跟踪的文件
git clean -f

# 同时删除未跟踪的目录
git clean -fd

# 连同被 .gitignore 忽略的文件一起删除
git clean -fdx

警告

git clean 删除的文件不在 Git 的管理范围内,无法恢复,执行前先用 -n 看一眼。