使用 Git 解决错误的指南(第 2 部分)

已发表: 2022-03-10
快速总结↬错误。 这些残忍的恶棍甚至没有停留在美丽的软件开发世界。 但是,虽然我们无法避免犯错,但我们可以学会撤消它们! 本文将展示适合您日常使用 Git 的工具。 您可能还想查看该系列的第一篇文章。

在我们“用 Git 解决错误”系列的第二部分中,我们将再次勇敢地直视危险:我准备了四个新的世界末日场景——当然,包括一些拯救我们脖子的聪明方法! 但在我们深入研究之前:查看之前关于 Git 的文章,了解更多帮助您消除 Git 错误的自救方法!

我们走吧!

使用 Reflog 恢复已删除的分支

你有没有删除过一个分支,不久之后,你意识到你不应该这样做? 万一您不知道这种感觉,我可以告诉您,这不是一种好感觉。 当你想到那个分支提交的所有辛勤工作,你现在丢失的所有有价值的代码时,悲伤和愤怒的混合物在你身上蔓延。

幸运的是,有一种方法可以使该分支起死回生——借助名为“Reflog”的 Git 工具。 我们在本系列的第一部分中使用了这个工具,但这里有一点更新:Reflog 就像一个日志,Git 记录了本地存储库中 HEAD 指针的每一次移动。 换句话说,不那么讨厌的话:任何时候你结帐、提交、合并、变基、挑选等,都会创建一个日记条目。 这使得 Reflog 在出现问题时成为完美的安全网!

我们来看一个具体的例子:

 $ git branch * feature/login master

我们可以看到我们当前已经签出了我们的分支feature/login 。 假设这是我们要删除的分支(无意中)。 然而,在我们这样做之前,我们需要切换到不同的分支,因为我们无法删除当前的 HEAD 分支!

 $ git checkout master $ git branch -d feature/login

我们有价值的特性分支现在已经消失了——我会给你一点时间来 (a) 了解我们错误的严重性和 (b) 哀悼一下。 等你擦干眼泪,我们得想办法把这根树枝带回来! 让我们打开 Reflog(只需键入git reflog )并查看它为我们存储的内容:

Git 的 Reflog 协议我们本地存储库中的所有主要操作
Git 的 Reflog 协议我们本地存储库中的所有主要操作。 (大预览)

这里有一些注释可以帮助您理解输出:

  • 首先,您需要知道 Reflog 按时间顺序对其条目进行排序:最新的条目位于列表顶部。
  • 最顶层(因此也是最新的)项目是我们在删除分支之前执行的git checkout命令。 它被记录在 Reflog 中,因为它是 Reflog 忠实记录的这些“HEAD 指针移动”之一。
  • 为了挽回我们的严重错误,我们可以简单地回到之前的状态——这也清楚地记录在 Reflog 中!

所以让我们尝试一下,通过创建一个新分支(名称为“丢失”分支),该分支从这个“之前”状态 SHA-1 哈希开始:

 $ git branch feature/login 776f8ca

瞧! 您会很高兴看到我们现在已经恢复了我们看似丢失的分支!

如果您使用的是“Tower”之类的 Git 桌面 GUI,您可以使用一个不错的快捷方式:只需按键盘上的CMD + Z即可撤消上一个命令——即使您刚刚暴力删除了一个分支!

像 Tower 这样的桌面 GUI 可以使撤消错误的过程更容易。
跳跃后更多! 继续往下看↓

将提交移动到不同的分支

在许多团队中,有一个协议是不提交长期运行的分支,如maindevelop :像这样的分支应该只通过集成(例如合并或变基)接收新的提交。 然而,当然,错误是不可避免的:尽管如此,我们有时会忘记并提交这些分支! 那么我们如何才能清理我们制造的烂摊子呢?

将提交移动到其正确的目标分支
我们的提交落在了错误的分支上。 我们如何将它移动到正确的目标分支? (大预览)

幸运的是,这些类型的问题很容易得到纠正。 让我们卷起袖子开始工作吧。

第一步是切换到正确的目标分支,然后过度使用cherry-pick命令移动提交:

 $ git checkout feature/login $ git cherry-pick 776f8caf

您现在将在所需的分支上进行提交,它应该首先出现在该位置。 惊人的!

但是还有一件事要做:我们需要清理一开始它意外降落的分支! 可以这么说, cherry-pick命令创建了提交的副本——但原始版本仍然存在于我们长期运行的分支中:

正确分支上的提交副本,但仍显示原件位于错误分支上
我们已经成功地在正确的分支上创建了提交的副本,但原件仍然在这里——在错误的分支上。 (大预览)

这意味着我们必须切换回我们长期运行的分支并使用git reset来删除它:

 $ git checkout main $ git reset --hard HEAD~1

如您所见,我们在这里使用git reset命令擦除错误的提交。 HEAD~1参数告诉 Git “返回 HEAD 之后的 1 个修订版”,有效地从该分支的历史记录中删除最顶层(在我们的例子中:不需要的)提交。

瞧:提交现在应该放在最开始的地方我们长期运行的分支是干净的——就好像我们的错误从未发生过一样!

编辑旧提交的消息

将拼写错误偷偷带入提交消息太容易了——直到很久以后才发现。 在这种情况下, git commit的旧--amend选项不能用来解决这个问题,因为它只适用于最后一次提交。 为了更正任何比这更早的提交,我们必须求助于一个名为“Interactive Rebase”的 Git 工具。

值得更改的提交消息
这是一个值得更改的提交消息。 (大预览)

首先,我们必须告诉 Interactive Rebase 我们要编辑提交历史的哪一部分。 这是通过给它提供一个提交哈希来完成的:我们想要操作的那个的提交。

 $ git rebase -i 6bcf266b

然后将打开一个编辑器窗口。 它包含我们在命令中作为交互式 Rebase 基础提供的提交之后的所有提交的列表:

显示我们在 Interactive Rebase 会话中选择进行编辑的提交范围
我们在 Interactive Rebase 会话中选择用于编辑的提交范围。 (大预览)

在这里,重要的是不要跟随你的第一个冲动:在这一步中,我们还没有编辑提交消息。 相反,我们只告诉 Git 我们想要对哪些提交进行什么样的操作。 非常方便的是,在此窗口底部的注释中有一个动作关键字列表。 对于我们的例子,我们用reword标记第 1 行(从而替换标准的pick )。

在此步骤中剩下要做的就是保存并关闭编辑器窗口。 作为回报,将打开一个新的编辑器窗口,其中包含我们标记的提交的当前消息。 现在终于到了进行编辑的时候了!

整个过程一目了然:

使用 Interactive Rebase 从头到尾编辑旧提交的消息。

纠正一个破碎的提交(以一种非常优雅的方式)

最后,我们将看一下fixup ,这是撤消工具的瑞士军刀。 简而言之,它允许您在事后修复损坏/不完整/不正确的提交。 它确实是一个很棒的工具,原因有两个:

  1. 问题是什么并不重要。
    您可能忘记了添加文件、应该删除了某些内容、进行了不正确的更改,或者只是拼写错误。 fixup适用于所有这些情况!
  2. 它非常优雅。
    我们对提交中的错误的正常、本能反应是生成一个的提交来解决问题。 这种工作方式,无论看起来多么直观,都会让你的提交历史看起来非常混乱,很快。 你有“原始”提交,然后这些小的“创可贴”提交修复了原始提交中出现的问题。 你的历史中充斥着小的、毫无意义的创可贴提交,这使得你很难理解你的代码库中发生了什么。
如果你不断用所谓的创可贴提交来修复小错误,你的提交历史可能很难阅读
使用“创可贴提交”不断修复小错误会使您的提交历史非常难以阅读。 (大预览)

这就是fixup的用武之地。它允许您仍然进行这种更正的创可贴提交。 但是神奇的地方来了:然后它将它应用于原始的、损坏的提交(以这种方式修复它),然后完全丢弃丑陋的创可贴提交!

Fixup 将您的更正应用于原始提交,然后处理多余的创可贴提交
Fixup 将您的更正应用于原始提交,然后处理多余的创可贴提交。 (大预览)

我们可以一起看一个实际的例子! 假设这里选择的提交被破坏了。

以优雅的方式修复选定的错误提交
选择的提交是不正确的——我们将以一种优雅的方式修复它。 (大预览)

假设我已经在一个名为error.html的文件中准备了可以解决问题的更改。 这是我们需要做的第一步:

 $ git add error.html $ git commit --fixup 2b504bee

我们正在创建一个新的提交,但我们告诉 Git 这是一个特殊的提交:它是对具有指定 SHA-1 哈希(在本例中为2b504bee )的旧提交的修复。

现在,第二步是启动 Interactive Rebase 会话——因为fixup属于 Interactive Rebase 的大工具集。

 $ git rebase -i --autosquash 0023cddd

关于这个命令有两件事值得解释。 首先,为什么我在这里提供0023cddd作为修订哈希? 因为我们需要在我们破碎的伙伴的父提交处开始我们的交互式 Rebase 会话。

其次, --autosquash选项有什么用? 这需要我们肩上的大量工作! 在现在打开的编辑器窗口中,一切都已经为我们准备好了:

交互式 Rebase 会话窗口
交互式 Rebase 会话窗口(大预览)

多亏了--autosquash选项,Git 已经为我们完成了繁重的工作:

  1. 它用fixup action 关键字标记了我们的小创可贴提交。 这样,Git 会将它与上面的提交直接结合,然后丢弃它。
  2. 它还相应地重新排序了行,将我们的创可贴提交直接移动到我们想要修复的提交下方(再次: fixup通过将标记的提交与上面的提交结合起来工作!)。

简而言之:除了关上窗户,我们无事可做!

让我们来看看最终的结果。

  • 以前损坏的提交已修复:它现在包含我们在创可贴提交中准备的更改。
  • 丑陋的创可贴提交本身已被丢弃:提交历史清晰且易于阅读——就好像根本没有发生任何错误一样。
一个干净的提交历史的例子
使用修复工具后的最终结果:干净的提交历史! (大预览)

知道如何消除错误是一种超能力

恭喜! 你现在可以在许多困难的情况下挽救你的脖子了! 我们无法真正避免这些情况:无论我们作为开发人员的经验如何,错误只是工作的一部分。 但既然您知道如何应对它们,您就可以用悠闲的心率来面对它们。

如果你想了解更多关于使用 Git 撤消错误的信息,我可以推荐免费的“Git 急救包”,这是一系列关于这个主题的短视频。

享受犯错的乐趣——当然,轻松地撤消它们!