Git 的回退功能非常强大,但因为有多个命令,初学者很容易混淆。我们来系统地梳理一下最核心的几个“回退”指令:git reset
、git revert
和 git restore
。
我会按照使用场景和安全级别来为你讲解。
核心区别:reset
vs revert
这是最重要的区别,理解了它就理解了 Git 回退的精髓:
git reset
(重置/回滚): 会修改提交历史。它像一台时间机器,直接把你的分支带回到过去某个点,后面的历史记录就“消失”了。- 适用场景:只在你自己的本地分支上使用。因为会修改历史,绝对不要在已经推送到远程的公共分支(如
main
,develop
)上使用! - 危险性:较高,尤其是
--hard
模式。
- 适用场景:只在你自己的本地分支上使用。因为会修改历史,绝对不要在已经推送到远程的公共分支(如
git revert
(撤销/反转): 不会修改历史,而是创建一个新的提交来抵消掉某个旧的提交。它像是在账本上写一笔“负数”来冲销之前的错误记录,而不是撕掉那一页。- 适用场景:安全,适用于任何分支,尤其是已经推送到远程的公共分支。这是团队协作中推荐的回退方式。
- 危险性:低。
1. git reset
:强大的本地时间机器
git reset
主要用来回退未推送的本地提交。它有三种模式,决定了它对你的工作区和暂存区的影响。
假设你的提交历史是 A -> B -> C
,当前在 C
(HEAD
指向 C
)。现在你想回退到 B
。
git reset <commit-B-hash>
# 或者更常用的,回退到上一个版本
git reset HEAD~1
三种模式:
--soft
(温柔模式)- 命令:
git reset --soft HEAD~1
- 效果:
- 提交历史: 回退到
B
。C
的提交被撤销。 - 暂存区 (Staging Area): 保留
C
提交时的所有更改,这些更改会处于“已暂存”状态。 - 工作区 (Working Directory): 保留
C
提交时的所有代码,文件内容不变。
- 提交历史: 回退到
- 一句话总结:撤销了提交,但保留了所有代码更改并放在暂存区,你可以马上重新提交。
- 应用场景:“我刚才的提交信息写错了,或者漏了几个文件,想把它们合并成一个新提交。”
- 命令:
--mixed
(默认模式)- 命令:
git reset HEAD~1
(不加参数时默认就是--mixed
) - 效果:
- 提交历史: 回退到
B
。 - 暂存区: 清空。
C
提交时的更改被移出暂存区。 - 工作区: 保留
C
提交时的所有代码,文件内容不变。
- 提交历史: 回退到
- 一句话总结:撤销了提交,也撤销了
git add
,但代码还在。 - 应用场景:“我刚才的提交不仅有问题,我还想重新检查一下到底哪些文件需要提交。”
- 命令:
--hard
(硬核/危险模式)- 命令:
git reset --hard HEAD~1
- 效果:
- 提交历史: 回退到
B
。 - 暂存区: 清空。
- 工作区: 代码被丢弃。你的文件会完全恢复到
B
提交时的状态。
- 提交历史: 回退到
- 一句话总结:彻底抹除
C
提交的所有痕迹,包括代码更改。 - ⚠️ 警告:这是一个破坏性操作!任何未提交的本地修改、以及
--hard
模式回退掉的提交内容,如果没有备份,就很难找回了。 - 应用场景:“我最近的几次提交完全是垃圾,我想彻底扔掉它们,从头再来。”
- 命令:
2. git revert
:安全的企业级“撤销”
当你发现一个已经推送到 main
分支的提交 C
引入了一个 Bug,你不能用 git reset
,因为这会搞乱团队其他成员的历史。这时就该用 git revert
。
如何使用:
假设你要撤销提交 C
(commit-C-hash
) 的更改。
git revert <commit-C-hash>
效果:
- Git 会创建一个新的提交
D
。 D
提交的内容,刚好是C
提交内容的反向操作。比如C
中添加了一行代码,D
就会删除那一行。- 你的提交历史会变成
A -> B -> C -> D
。 - Git 会自动打开编辑器让你填写这次撤销操作的提交信息。
- Git 会创建一个新的提交
一句话总结:用一次新的、正确的提交,来“纠正”一次旧的、错误的提交。
应用场景:“线上代码出 Bug 了,需要立刻回滚某个已经发布的提交,同时保持历史记录的清晰和团队协作的稳定。”
3. git restore
& git checkout --
:撤销工作区的修改
这两个命令主要用于处理尚未提交的更改。
git restore
是较新的命令,语法更清晰,推荐使用。
场景一:撤销对工作区文件的修改(还没 git add
)
你不小心改乱了一个文件,想把它恢复到上次提交时的样子。
# 新语法 (推荐)
git restore <file_name>
# 旧语法
git checkout -- <file_name>
效果:file_name
在你工作区的修改会被丢弃,恢复成和暂存区/上次提交一样的版本。
场景二:把文件从暂存区撤销(已经 git add
,但还没 git commit
)
你用 git add
把一个不想提交的文件加到了暂存区。
# 新语法 (推荐)
git restore --staged <file_name>
# 旧语法
git reset HEAD <file_name>
效果:文件会从暂存区移除,但工作区的修改内容仍然保留。
如何选择:一个简单的决策流程
这次回退需要影响公共历史吗?(即,代码已
push
到main
/develop
)- 是 ->
git revert
(安全第一) - 否 -> 继续看第 2 步。
- 是 ->
你想撤销的是已经
commit
的提交吗?- 是 ->
git reset
- 想保留代码并重新提交? ->
reset --soft
- 想保留代码但重新暂存? ->
reset --mixed
- 想彻底丢掉代码? ->
reset --hard
⚠️
- 想保留代码并重新提交? ->
- 否 (只是工作区或暂存区的修改) -> 继续看第 3 步。
- 是 ->
你想撤销的是
git add
操作吗?- 是 ->
git restore --staged <file>
- 否 (只是想丢弃文件的本地修改) ->
git restore <file>
- 是 ->
记住这个流程,你就能在各种场景下选择最合适、最安全的回退指令了。