使用 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 ),看看它为我们准备了什么。

这里有一些评论来帮助你理解输出。
- 首先,你需要知道Reflog是按时间顺序排序的:最新的项目在列表的顶部。
- 最上面的(因此也是最新的)项目是我们在删除分支之前执行的
git checkout命令。它被记录在 Reflog 中,因为它是 Reflog 尽职地记录的 "HEAD 指针移动 "之一。 - 要撤销我们的严重错误,我们可以简单地返回到之前的状态--这在 Reflog 中也被干净利落地记录下来了!因此,让我们试试这个方法。
因此,让我们尝试一下,创建一个新的分支(名称为 "丢失 "的分支),从这个 "之前 "状态的 SHA-1 哈希值开始。
$ git branch feature/login 776f8ca
然后就可以了!你会很高兴地看到,我们现在已经恢复了我们看似丢失的分支🎉
如果你使用的是像 "Tower "这样的Git桌面GUI,你可以使用一个很好的快捷方式:只需在键盘上点击CMD+Z,就可以撤销最后一条命令--即使你刚刚粗暴地删除了一个分支

幸运的是,这些类型的问题可以很容易地得到纠正。让我们卷起袖子,开始工作吧。
第一步是切换到正确的目标分支,然后用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 选项不能用来解决这个问题,因为它只对最后一次提交起作用。要修正任何比这更早的提交,我们必须借助于一个叫做 "交互式重做 "的 Git 工具。

首先,我们要告诉 Interactive Rebase 我们要编辑哪一部分的提交历史。这可以通过给它提供一个提交哈希值来实现:我们要操作的提交的父级提交。
$ git rebase -i 6bcf266b
然后会打开一个编辑器窗口。它包含了一个列表,列出了我们在命令中提供的作为交互式重做基础的那个提交之后的所有提交。

在这里,重要的是你不要跟随你的第一次冲动:在这一步,我们还没有编辑提交信息。相反,我们只告诉 Git 我们想对哪个(些)提交做什么样的操作。很方便地,在这个窗口底部的注释中,有一个行动关键词的列表。在我们的例子中,我们用reword (从而取代了标准的pick )来标记第1行。
这一步所要做的就是保存并关闭编辑器窗口。作为回报,一个新的编辑器窗口将被打开,其中包含我们标记的提交的当前信息。现在终于到了我们进行编辑的时候了!
下面是整个过程,让你一目了然。

这就是fixup 的作用。它允许你仍然进行这种纠正性的创可贴提交。但神奇之处在于:它可以将其应用于原始的、被破坏的提交(以这种方式修复它),然后完全抛弃这个丑陋的创可贴式提交

我们可以一起看一个实际的例子比方说,这里选择的提交是坏的。

又假设我在一个名为error.html 的文件中准备了一些修改,可以解决这个问题。这里是我们需要做的第一步。
$ git add error.html
$ git commit --fixup 2b504bee
我们要创建一个新的提交,但我们要告诉 Git 这是一个特殊的提交:它是对指定的 SHA-1 哈希值(本例中为2b504bee )的旧提交的修复。
第二步,现在是启动一个交互式重做会话--因为fixup 属于交互式重做的大工具集。
$ git rebase -i --autosquash 0023cddd
关于这个命令,有两件事值得解释。首先,为什么我在这里提供0023cddd 作为修订哈希值?因为我们需要在断裂的同伴的父提交处开始我们的交互式重塑会话。
第二,--autosquash 这个选项有什么用?它为我们分担了大量的工作。在现在打开的编辑器窗口中,一切都已经为我们准备好了。

多亏了--autosquash 选项,Git 已经为我们完成了繁重的工作。
- 它用
fixup动作关键字标记了我们的小创可贴提交。这样一来,Git 就会把它和上面的提交合并,然后丢弃它。 - 它还相应地重排了行数,将我们的创可贴提交移到了我们要修复的提交的正下方(再次强调:
fixup,将被标记的提交与上方的提交合并!)。
简而言之:除了关闭窗口,我们没有什么可做的。
让我们最后看一下最终的结果。
- 之前被破坏的提交被修复了:它现在包含了我们在创可贴提交中准备的修改。
- 丑陋的 "创可贴 "提交本身也被丢弃了:提交历史干净整洁,易于阅读--就像根本没有发生过错误一样。

知道如何撤消错误是一种超能力
祝贺你!你现在可以拯救你的脖子了。你现在可以在很多困难的情况下保住自己的脖子了我们无法真正避免这些情况:无论我们作为开发者多么有经验,错误只是工作的一部分。但现在你知道如何处理它们,你就可以以轻松的心态面对它们。💚