在 Git 版本控制中,撤销更改是开发中常见的操作。无论是误提交了代码、需要回退到某个历史状态,还是想撤销已经推送到远程仓库的提交,Git 提供了多种工具来处理这些场景。本文将重点讲解 git reset 和 git revert 的区别与用法,帮助你根据实际需求选择合适的工具,高效管理提交历史。
背景:为什么需要撤销操作?
在使用 Git 的过程中,你可能会遇到以下情况:
- 提交了错误的代码,想完全撤销。
- 提交历史中包含不必要的提交,想清理。
- 已经推送到远程仓库的提交需要撤销,但不能影响其他开发者。
- 需要回退到某个历史状态,但保留后续修改。
Git 提供了两种主要命令来处理撤销操作:git reset 和 git revert。它们虽然都能“撤销”提交,但作用和适用场景截然不同。
Git Reset 和 Git Revert 的核心区别
1. 功能和行为
- git reset:- 作用:将当前分支的 HEAD回退到指定状态,并(可选地)修改工作目录和暂存区。
- 特点:重写提交历史,直接移除指定提交及之后的所有提交。
- 结果:被撤销的提交从历史中消失,仿佛从未发生。
- 适用场景:适合本地开发中清理未推送的提交,或者完全回退到某个状态。
 
- 作用:将当前分支的 
- git revert:- 作用:创建一个新的提交,反向应用指定提交的更改。
- 特点:不修改现有历史,而是通过新增一个“反向提交”来撤销更改。
- 结果:提交历史中会多一个新的提交,记录撤销操作。
- 适用场景:适合撤销已推送到远程仓库的提交,尤其是在多人协作项目中。
 
2. 历史记录的影响
- git reset:重写历史,删除指定提交及之后的所有提交。
- git revert:保留历史,新增一个反向提交,历史记录线性增长。
3. 协作安全性
- git reset:不安全。如果分支已推送,强制推送(- git push --force)会覆盖远程历史,可能导致其他开发者的工作丢失。
- git revert:安全。新增提交不会破坏历史,其他开发者可以正常拉取和合并。
4. 适用场景对比
| 特性 | git reset | git revert | 
|---|---|---|
| 历史记录 | 重写历史,删除提交 | 保留历史,新增反向提交 | 
| 适用场景 | 本地未推送的提交 | 已推送的提交或多人协作 | 
| 协作安全性 | 不安全(需谨慎使用 --force) | 安全 | 
| 操作复杂度 | 简单,但可能丢失修改 | 稍复杂,需处理冲突 | 
Git Reset 的用法
git reset 有三种主要模式,根据参数不同,影响范围也不同:
- --soft:仅移动- HEAD,不修改暂存区和工作目录。
- --mixed(默认):移动- HEAD,重置暂存区,但不修改工作目录。
- --hard:移动- HEAD,重置暂存区和工作目录,所有更改丢失。
示例:撤销未推送的提交
假设当前提交历史如下:
* 5d8f9a2 (HEAD -> feature) fix typo
* e7b3c4d add new feature
* 2a1b3c4 initial setup
* 9c8d7e6 (main) initial commit你发现 5d8f9a2(fix typo)提交是错误的,想撤销。
1. 使用 --soft 撤销
- 目标:撤销提交,但保留更改在暂存区。
- 命令:
git reset --soft HEAD^ # 回退到上一个提交
- 结果:
- 提交历史变为:
- e7b3c4d (HEAD -> feature) add new feature
- 2a1b3c4 initial setup
- 9c8d7e6 initial commit
- fix typo的更改仍在暂存区,可以重新提交:- git status- 输出: - Changes to be committed: ...
 
- 提交历史变为:
2. 使用 --mixed 撤销(默认)
- 目标:撤销提交,将更改放回工作目录(未暂存)。
- 命令:
git reset --mixed HEAD^ # 或直接 git reset HEAD^
- 结果:
- 提交历史同上。
- 更改回到工作目录,未暂存:
git status输出: Changes not staged for commit: ...
 
3. 使用 --hard 撤销
- 目标:完全撤销提交,丢弃所有更改。
- 命令:
git reset --hard HEAD^
- 结果:
- 提交历史同上。
- 所有更改(fix typo的修改)被丢弃,无法恢复。
- 警告:使用 --hard会永久丢失未保存的更改,谨慎操作。
 
4. 撤销到更早的提交
如果需要回退到更早的提交(例如 2a1b3c4):
git reset --hard 2a1b3c4- 结果:
* 2a1b3c4 (HEAD -> feature) initial setup * 9c8d7e6 initial commit- e7b3c4d和- 5d8f9a2都被移除。
 
Git Revert 的用法
git revert 通过创建一个反向提交来撤销指定提交的更改,适合已推送的提交。
示例:撤销已推送的提交
假设提交历史如下,且已推送:
* 5d8f9a2 (HEAD -> feature, origin/feature) fix typo
* e7b3c4d add new feature
* 2a1b3c4 initial setup
* 9c8d7e6 (main) initial commit你发现 e7b3c4d(add new feature)有问题,需要撤销。
1. 运行 git revert
- 命令:
git revert e7b3c4d
- 
过程: - Git 会创建一个新的提交,反向应用 e7b3c4d的更改。
- 可能会打开编辑器,让你编辑新提交信息,默认信息为:
Revert "add new feature"
 This reverts commit e7b3c4d... - 保存并关闭。
- Git 会创建一个新的提交,反向应用 
2. 检查结果
- 命令:
git log --oneline
- 输出:
* a1b2c3d (HEAD -> feature) Revert "add new feature" * 5d8f9a2 fix typo * e7b3c4d add new feature * 2a1b3c4 initial setup * 9c8d7e6 initial commit
- 说明:
- 新提交 a1b2c3d反向应用了e7b3c4d的更改。
- 历史记录完整保留。
 
- 新提交 
3. 推送到远程
- 命令:
git push
- 结果:远程分支更新,其他开发者可以正常拉取。
4. 撤销多个提交
如果需要撤销连续的提交(例如 e7b3c4d 和 5d8f9a2):
git revert e7b3c4d..5d8f9a2- 注意:需要按时间倒序列出范围(从旧到新)。
- Git 会为每个提交创建一个反向提交。
解决冲突
git reset 的冲突
- git reset通常不会产生冲突,因为它直接重写历史。
- 但如果有未提交的更改(工作目录或暂存区),可能需要先处理:
git stash # 暂存更改 git reset --hard HEAD^ git stash pop # 恢复更改
git revert 的冲突
- 如果 git revert遇到冲突,Git 会暂停并提示:error: could not revert e7b3c4d... add new feature hint: after resolving the conflicts, mark the corrected paths hint: with 'git add' or 'git rm ' hint: and commit the result with 'git commit' 
- 解决步骤:
- 打开冲突文件,手动解决冲突。
- 添加解决后的文件:
git add
- 继续 revert:
git revert --continue
- 如果想放弃:
git revert --abort
 
注意事项
- 备份:
- 在使用 git reset --hard或推送前,创建备份分支:git branch backup-branch
 
- 在使用 
- 远程协作:
- 如果使用 git reset后强制推送(git push --force),确保通知团队成员,避免覆盖他人工作。
- git revert是协作中的首选。
 
- 如果使用 
- 恢复误操作:
- 如果 git reset --hard后想恢复,使用git reflog:git reflog输出示例: 5d8f9a2 HEAD@{0}: reset: moving to HEAD^ e7b3c4d HEAD@{1}: commit: fix typo恢复: git reset --hard 5d8f9a2
 
- 如果 
实践场景对比
场景 1:撤销本地未推送的提交
- 历史:
* 5d8f9a2 (HEAD -> feature) fix typo * e7b3c4d add new feature * 2a1b3c4 initial setup
- 操作:使用 git reset:git reset --soft HEAD^ # 撤销 fix typo,保留更改 git commit -m "Add new feature and fix typo"
- 结果:
* a1b2c3d (HEAD -> feature) Add new feature and fix typo * 2a1b3c4 initial setup
场景 2:撤销已推送的提交
- 历史:
* 5d8f9a2 (HEAD -> feature, origin/feature) fix typo * e7b3c4d add new feature * 2a1b3c4 initial setup
- 操作:使用 git revert:git revert e7b3c4d # 撤销 add new feature git push
- 结果:
* b2c3d4e (HEAD -> feature, origin/feature) Revert "add new feature" * 5d8f9a2 fix typo * e7b3c4d add new feature * 2a1b3c4 initial setup
总结
- git reset:适合本地开发,清理未推送的提交,重写历史,灵活但需谨慎。
- git revert:适合协作环境,撤销已推送的提交,保留历史,安全但可能增加历史记录。
- 选择建议:
- 如果提交未推送,使用 git reset更高效。
- 如果提交已推送,使用 git revert更安全。
 
- 如果提交未推送,使用 
- 最佳实践:
- 定期备份分支。
- 熟悉 git reflog以恢复误操作。
- 在多人协作中优先使用 git revert。
 
通过熟练掌握 git reset 和 git revert,你可以灵活应对各种撤销需求,提升 Git 使用的专业性。欢迎在评论区分享你的使用经验!
补充理解
git revert 操作可以理解为,针对某次commit A的内容进行反向的一次新的commit B,以还原为A提交之前的代码状态。且A因为比B旧,所以其他分支有A的提交再合并进来也没用,会被B覆盖。
如果想还原被git revert还原的commit内容,需要对revert的那次commit(如上述的commit B)再进行一次git revert操作,即可还原为最初的状态(包含commit A内容的状态)。
git reset则是对分支进行操作,直接reset到某次commit节点的状态(回到链表的某个节点),中间的所有commit都直接没了。
 
    









Comments NOTHING