Git Revert 与 Git Reset 的区别:哪一个更适合你? – wiki词典

Git Revert 与 Git Reset 的区别:哪一个更适合你?

在 Git 的世界里,撤销更改是日常操作的一部分。无论是因为提交了错误的代码、需要回滚一个功能,还是仅仅想清理一下提交历史,Git 都提供了强大的工具来应对这些情况。其中,git revertgit reset 是最常用但也是最容易混淆的两个命令。

它们都能“撤销”历史,但方式却截然不同。错误地使用它们,尤其是在团队协作中,可能会导致严重的仓库混乱。本文将详细解析这两个命令的工作原理、应用场景和核心区别,帮助你做出正确的选择。


核心概念:修改历史 vs. 保留历史

理解这两个命令的关键在于它们如何对待 Git 的提交历史:

  • git reset: 重写历史。它会将你的分支“倒带”到过去的某个提交,让之后的提交看起来就像从未发生过一样。这是一种具有破坏性的操作。
  • git revert: 追加历史。它会创建一个新的提交,这个新提交的内容正好与你想要撤销的某个提交相反。它不会删除任何历史,而是在现有历史的基础上进行“反向操作”。

让我们更深入地探讨每一个命令。


深入了解 git reset:时间的倒带机

git reset 的主要作用是移动 HEAD 指针,并且可以选择性地修改暂存区(Staging Area)和工作目录(Working Directory)。它有三种主要模式:

1. git reset --soft <commit>

  • 作用:只移动 HEAD 指针到指定的 <commit>
  • 状态:暂存区和工作目录的内容都不会改变。从旧 HEAD 到新 HEAD 之间的所有更改都会被放入暂存区。
  • 场景:当你想要合并多个零散的提交为一个时。例如,git reset --soft HEAD~3 会将最近 3 次的提交合并,所有更改都放在暂存区,你可以创建一个新的、更清晰的提交。

2. git reset --mixed <commit> (默认模式)

  • 作用:移动 HEAD 指针,并重置暂存区。
  • 状态:工作目录的内容不会变。但从旧 HEAD 到新 HEAD 之间的更改会以“未暂存”的状态保留在工作目录中。
  • 场景:当你发现最近的提交有问题,想保留代码修改,但重新调整提交内容时。git reset HEAD~1 会撤销上一次提交,并将更改放回工作目录,让你重新检查和提交。

3. git reset --hard <commit>

  • 作用:移动 HEAD 指针,重置暂存区,并重置工作目录。
  • 状态:这是一个彻底的、具有破坏性的操作。所有未提交的更改(包括暂存区的)以及从旧 HEAD 到新 HEAD 的所有提交内容都将被永久丢弃。你的代码库会完全回到 <commit> 的状态。
  • 场景:当你确定最近的提交完全是错误的,并且想彻底抛弃它们时。请务必谨慎使用!

git reset 的黄金法则

永远不要 reset 已经推送到远程共享分支(如 maindevelop)的提交。

因为 reset 会重写历史,如果你 reset 了一个共享分支,团队其他成员的本地历史将与远程历史产生分歧。当他们尝试拉取更新时,会引发复杂的合并冲突,给整个团队带来麻烦。git reset 最适合用于你个人私有分支或者尚未推送的本地提交。


深入了解 git revert:安全的反向操作

reset 不同,git revert 是一种安全、非破坏性的“撤销”方式。它不会删除或修改任何现有的提交。

工作原理

当你执行 git revert <commit> 时,Git 会:

  1. 分析 <commit> 所做的更改(比如添加了哪些行,删除了哪些行)。
  2. 创建一个全新的提交,这个新提交执行与 <commit> 完全相反的操作(删除它添加的行,加回它删除的行)。
  3. 你的提交历史中会增加一个类似 Revert "commit message" 的新提交,而原始提交依然完好无损地存在。

git revert 的主要优势

  • 安全:它不改变项目历史,对于已经推送到远程的提交,这是唯一的安全撤销方法。
  • 清晰:它在提交日志中明确地记录了“撤销”这一行为。任何人查看历史,都能清楚地看到某个功能被引入,后来又被撤销了,原因是什么。
  • 团队友好:团队成员只需像平常一样 git pull 即可同步这个撤销操作,不会造成历史冲突。

git revert 的应用场景

当你需要撤销一个已经分享给团队的提交时,git revert 是不二之选。例如,一个刚上线的功能被发现有严重 Bug,需要立即回滚。

“`bash

找到导致问题的提交哈希值

git log

假设有问题的提交是 a1b2c3d4

git revert a1b2c3d4

Git 会创建一个新提交来撤销 a1b2c3d4 的更改

然后你可以安全地将这个 revert 提交推送到远程

git push
“`


对比总结:Revert vs. Reset

特性 git reset git revert
历史记录 重写/删除 历史,具有破坏性 追加到历史,是安全的、非破坏性的
操作对象 作用于分支的指针,将其移动到旧的提交 作用于某个特定提交,创建一个反向操作的新提交
协作影响 非常危险,绝不能用于共享分支的已推送提交 非常安全,是撤销共享分支提交的标准做法
常见用途 清理本地未推送的提交,合并提交 撤销公共的、已推送的提交,进行安全回滚
结果 “被撤销”的提交从分支历史上消失 “被撤销”的提交和“撤销”提交都存在于分支历史上

场景分析:我该用哪个?

场景一:我刚在本地提交了一些代码,还没 push,但发现提交信息写错了,或者代码有小问题。

  • 答案git reset
  • 操作:使用 git reset --soft HEAD~1,这会撤销上一次提交但保留所有更改在暂存区。然后你可以重新 git commit 并附上正确的信息和代码。

场景二:一个功能分支已经合并到 main 分支并已推送,现在发现这个功能导致了严重的 Bug。

  • 答案git revert
  • 操作:找到合并该功能分支的那个 Merge Commit,然后 git revert -m 1 <merge-commit-hash>。这会创建一个新的提交,撤销合并带来的所有更改,从而安全地回滚功能。

场景三:我正在自己的私有分支上开发,连续做了好几个实验性的提交,现在想把它们全部扔掉,回到几天前的某个状态。

  • 答案git reset
  • 操作git reset --hard <commit-hash-to-go-back-to>再次警告:这将永久删除这些提交以及所有未保存的本地更改。因为这是你的私有分支,所以不会影响到任何人。

结论

选择 git revert 还是 git reset,取决于一个核心问题:你要撤销的提交是否已经被分享给了他人?

  • 是,已经分享/推送了 -> 使用 git revert 来保证历史的完整性和团队协作的流畅。
  • 否,还在我的本地 -> 使用 git reset 来自由、灵活地清理和重组你的提交历史。

记住这条简单的规则,你就能在各种场景下充满自信地使用 Git 来管理你的代码版本,让它成为你的得力助手,而不是混乱的来源。

滚动至顶部