Git reset 与 revert 的区别:如何安全撤销错误提交
前言
使用 Git 开发项目时,偶尔会遇到提交完成后才发现代码有问题的情况。比如提交了错误配置、漏改了文件,或者某次功能修改需要回滚。
Git 提供了 reset 和 revert 两种常见的撤销方式。它们看起来都能撤销提交,但对提交历史的处理方式完全不同。
简单来说,reset 可以移动当前分支的位置,而 revert 会通过新的提交撤销之前的修改。实际使用时,最重要的是区分代码是否已经推送到远程仓库。
一、先查看提交历史
在执行撤销操作前,建议先查看最近几次提交:
git log --oneline -5
假设输出如下:
c3d4e5f 修复登录接口
b2c3d4e 更新用户配置
a1b2c3d 初始化项目
每条记录前面的是提交哈希,可以用来指定需要撤销的提交。
如果只是想撤销最新一次提交,也可以直接使用 HEAD。
HEAD 通常指向当前检出的提交,HEAD~1 则表示沿第一父提交向前移动一次。
二、git reset 的三种常用模式
reset 最容易混淆的是 soft、mixed 和 hard。它们的区别主要体现在是否保留暂存区和工作区的修改。
1. --soft:撤销提交,保留暂存内容
git reset --soft HEAD~1
执行后,当前分支会回到上一个提交,但暂存区和工作区保持原样。
也就是说,原来最新提交中的修改通常仍然处于已暂存状态,可以直接重新提交。
适合刚提交完才发现提交说明写错,或者想把几个提交重新整理的情况。
2. --mixed:撤销提交,保留文件修改
git reset --mixed HEAD~1
mixed 是 reset 的默认模式,因此也可以直接写成:
git reset HEAD~1
执行后,当前分支回到上一个提交,暂存区会重置,但工作区中的文件修改仍然保留。
适合想重新选择需要提交的文件或修改部分代码的场景。
3. --hard:同时重置暂存区和工作区
git reset --hard HEAD~1
这个操作比较危险。
它会把当前分支、暂存区和受影响的工作区文件都恢复到指定提交的状态。未提交的已跟踪文件修改可能直接丢失,部分妨碍写入的未跟踪文件也可能被删除。
因此,不建议在有重要未提交修改时随意执行。
如果只是提交信息写错,或者想重新整理提交,通常没有必要使用 hard。
三、git revert:通过新提交撤销修改
如果错误提交已经推送到远程仓库,并且可能被其他人拉取,更适合使用 revert。
例如:
git revert HEAD
Git 会根据指定提交产生反向修改,并在正常完成时创建一个新的提交。
假设原来的历史是:
A --- B --- C
如果撤销 C,历史会变为:
A --- B --- C --- D
其中 D 是用于撤销 C 的新提交。
这样既能取消之前的代码修改,又能保留完整的历史记录,其他开发者也能看到这次回滚操作。
如果需要撤销更早的某个普通提交,也可以指定它的哈希:
git revert c3d4e5f
不过需要注意,如果后续提交修改过相同的代码,revert 可能发生冲突。它并不能保证任何历史提交都能无冲突地撤销。
四、reset 和 revert 怎么选?
可以按照下面的原则理解:
| 对比项 | git reset | git revert |
|---|---|---|
| 是否移动当前分支 | 是 | 通常不会回退分支 |
| 是否保留被撤销的提交记录 | 提交可能不再位于当前分支历史中 | 保留 |
| 是否生成新提交 | 通常不生成 | 正常完成时生成 |
| 已经推送到共享分支 | 不建议随意回退 | 通常更合适 |
| 本地整理未推送的提交 | 比较方便 | 一般没必要 |
这里还有一个细节:reset 并不一定意味着提交对象立刻被 Git 删除。旧提交可能仍然能够通过 reflog 等方式找到,但不应该把这种恢复机制当作可靠备份。
五、实际例子:刚提交就发现漏了文件
假设刚执行:
git commit -m "更新配置"
随后发现还有一个配置文件忘记加入。
如果提交还没有推送,可以直接补充文件,然后修改最后一次提交:
git add config.yaml
git commit --amend --no-edit
这通常比先 reset 再重新提交更加方便。
如果希望重新整理全部修改,也可以使用:
git reset --soft HEAD~1
然后补充文件并重新提交。
需要注意,amend 也会改写提交历史,所以对于已经推送到多人共享分支的提交,同样需要谨慎。
六、撤销已经推送的错误提交
假设一次错误修改已经推送到了远程 main 分支,而且其他开发者可能已经拉取。
这时不要直接 reset 后强制推送覆盖远程历史。
可以先确认工作区没有未处理的修改:
git status
然后撤销指定的普通提交:
git revert <提交哈希>
确认新提交内容正确后,再按正常流程推送。
如果 revert 过程中发生冲突,可以先解决冲突,再使用:
git revert --continue
如果决定放弃本次撤销操作,可以使用:
git revert --abort
对于合并提交,revert 还涉及主线父提交的选择,操作比普通提交更复杂,不建议直接套用上述命令。
七、操作前的几个建议
第一,执行 reset 前先检查 git status,确认有没有需要保留的工作区修改。
第二,已经推送到共享分支的提交,优先考虑 revert,避免无意中改写其他人的提交历史。
第三,--hard 并不是更彻底、更推荐的撤销方式。它只适合明确需要丢弃相关修改的场景。
第四,如果只是想取消某个文件的暂存,不需要移动分支,可以使用:
git restore --staged config.yaml
根据实际目的选择命令,比记住一堆 reset 参数更加重要。
总结
Git reset 和 revert 都能用于撤销修改,但它们适用的场景不同。
如果是本地尚未推送的提交,reset 可以方便地调整分支历史,并根据 soft、mixed、hard 决定保留哪些修改。
如果提交已经进入共享分支,revert 通常更加稳妥,因为它通过新提交撤销修改,不需要覆盖原来的提交历史。
日常开发中,建议遵循一个简单原则:未推送的提交可以考虑 reset,已推送的共享提交优先考虑 revert。
- 点赞
- 收藏
- 关注作者
评论(0)