软件开发的世界提供了无限种出错的方式:删除错误的东西、代码陷入死胡同、提交信息中充斥着拼写错误,这些仅仅是其中一小部分。
幸运的是,当我们使用版本控制时,Git 为我们提供了可靠的安全网。当然,你我并不需要安全网,因为我们从不犯错,对吧?好吧,好吧,为了其他人的利益,让我们来了解一些 Git 中的“撤销”工具,它们可以帮助我们避免自误。
修正最后一次提交
提交错误很容易发生。典型的例子是:在提交信息中出现拼写错误。另一个例子是:忘记将更改添加到暂存区。在很多情况下,我们会在按下回车键后立即意识到自己的错误,这很自然。
幸运的是,Git 使修正最后一次提交变得非常容易。假设我们刚刚对以下命令按下了回车键
git commit -m "Massage full of typohs"
并且(就好像这个拼写错误还不够糟糕一样)假设我们还忘记将另一个已更改的文件添加到暂存区。我们可以使用以下两条命令来修正这两个错误
git add forgotten-changes.js
git commit --amend -m "A sensible message"
神奇之处在于--amend
标志:当在提交中使用它时,Git 会修正最后一次提交,并包括任何暂存的更改和新的信息。
不过,要提醒一点:只对尚未推送到远程存储库的提交使用--amend
。原因是 Git 会用修正后的版本替换原始的错误提交。之后,看起来原始提交从未发生过。没错,这对于隐藏错误很有用,但前提是我们还没有在远程服务器上发布这个错误。
撤销本地更改
每个人都经历过这样的日子:整个上午都在不停地敲代码,最后才承认自己过去的几个小时浪费了时间。必须重新开始,撤销大部分(或全部)的工作。
但这正是我们使用 Git 的原因之一——能够尝试各种东西,而无需担心会破坏任何东西。
让我们以一个示例情况为例
git status
modified: about.html
deleted: imprint.html
modified: index.html
现在,假设这是上面提到的那些浪费时间的代码编写日之一。我们应该避免修改about.html,并且不应该删除imprint.html。我们现在想要丢弃这些文件中当前的更改,同时保留在index.html中完成的出色工作。git checkout
命令可以帮助我们解决这个问题。相反,我们必须更明确地指定要签出的文件,如下所示
git checkout HEAD about.html imprint.html
这条命令将about.html和imprint.html恢复到它们最后一次提交的状态。Phew,我们逃过一劫!
我们可以更进一步,丢弃更改文件中特定行的内容,而不是丢弃整个文件!我承认,在命令行中进行操作相当复杂,但使用像Tower这样的桌面 Git 客户端是一个很好的选择

对于那些真正糟糕的日子,我们可能需要使用强大的武器,即
git reset --hard HEAD
虽然我们只用checkout
恢复了特定文件,但这项命令会重置我们的整个工作副本。换句话说,reset
会将整个项目恢复到最后一次提交的状态。与--amend
类似,在使用checkout
和reset
时需要注意的一点是:使用这些命令丢弃本地更改是不可逆转的!这些更改从未提交到存储库,因此它们无法恢复也是合乎逻辑的。最好确保你确实想要丢弃这些更改,因为无法恢复!
撤销和回退较旧的提交
在很多情况下,我们直到很久以后,在提交到存储库很久以后才意识到错误。

我们如何才能删除那个错误的提交?答案是我们不应该删除... 至少在大多数情况下不应该删除。即使是在“撤销”操作中,Git 通常也不会真正删除数据。它会通过添加新数据来进行修正。让我们来看看使用我们的“错误提交”示例如何实现这一点
git revert 2b504bee
通过对那个错误的提交使用git revert
,我们并没有删除任何东西。恰恰相反

Git 会自动创建一个新的提交,其中包含撤销“错误”提交的效果的更改。因此,如果我们最初有三个提交,并且试图修正中间那个提交,现在我们总共有了四个提交,新添加的提交会修正我们用revert
目标指定的提交。
恢复项目的先前版本
另一种用例是我们想要恢复项目的先前版本。我们可能并不想仅仅撤销或回退提交历史记录中的特定修订,而是真正想倒流时间,回到特定修订。
在以下示例场景中,我们将声明“C2”之后的所有提交都是不必要的。我们想要回到“C2”提交的状态,并将此过程中之后的所有提交都忘掉

必要的命令(至少部分)你已经通过我们已经讨论的内容了解了
git reset --hard 2b504bee
这会告诉git reset
我们想要返回的提交的 SHA-1 哈希值。提交 C3 和 C4 随后会从项目的提交历史记录中消失。
如果你使用的是 Git 客户端,比如 Tower,那么git revert
和git reset
都可以在提交项的上下文菜单中找到

删除提交、恢复已删除的分支、处理冲突等等等
当然,在软件项目中还有很多其他方法会导致错误。但幸运的是,Git 还提供了更多工具来修正混乱。
如果你想了解更多关于我们在本文中讨论的场景或其他主题的信息,比如如何在分支之间移动提交、删除旧提交、恢复已删除的分支或优雅地处理合并冲突,请查看我和 Tower 团队的其他成员创建的“Git 急救手册”项目。 这是一份完全免费的指南,包含 17 个视频和一个方便的备忘单,你可以下载并将其放在你的机器旁。

同时,祝你撤销愉快!
好文章!然而,我们遵循的原则是,我们始终修复问题,从不回退或恢复,因此从不使用这些命令,因为如果你不知道自己在做什么,很容易陷入混乱。
感谢你的反馈,John!的确,使用这些命令需要“知道自己在做什么”。然而,在很多情况下,一个非常简单的规则可以保护你免受最糟糕的情况:”不要修改已经推送到远程的提交历史记录“。
只要你使用这些工具来清理你的本地提交历史记录,你就可以全力以赴 ;-)