有没有办法恢复在rebase期间意外跳过的提交?

时间:2014-08-08 21:34:10

标签: git git-rebase

当在rebase操作期间意外跳过有用的提交时,是否有希望Git保留可以重新应用它的引用?

这是一个包含大量二进制文件的非交互式rebase,我使用git rebase --skip进行了很长时间的快乐触发情绪,因此根本没有错误信息,只是一种糟糕的态度。

这似乎是一个硬盘崩溃的恢复方案,但不应该追逐幻像inode,应该有一种方法来过滤.git/objects中丢失的树对象并让它们恢复活着。

2 个答案:

答案 0 :(得分:4)

当您运行git rebase(交互式或非交互式)时,git基本上会执行一系列cherry-pick操作,以将原始提交链复制到新链。让我们使用o作为原始提交,并为分支branch的分支main绘制提交图片段:

        o1 - o2 - o3 - o4   <-- branch
      /
..- * - x                   <-- main

现在,您可以运行git rebase将所有旧的o提交复制到新的n提交,但基于xmain的提示,而不是基于*,旧的合并基点。为了使它更像发生的事情,让我们意外地&#34;留下一个:

        o1 - o2 - o3 - o4   <-- ???
      /
..- * - x                   <-- main
          \
            n1 - n3 - n4    <-- branch

上面的???标签表示指向或指向提交o4的git引用(branch-name,tag-name或任何其他合适的标签)。 只要有一个指向他们的名字,所有旧提交仍然在那里。如果有没有名称,他们仍会坚持到git gc清除它们(但你不希望这种情况发生,所以不要运行{{ 1}} :-))。

那么重要的问题是:&#34;我们(和git)可以使用哪些名称来查找git gc?&#34;事实证明至少有两个:

  • &#34; reflog&#34;和
  • 中的一个或多个
  • 一个拼写o4

ORIG_HEAD一个是最容易使用的,但该名称也被其他命令(例如ORIG_HEAD)使用,因此您必须查看它是否仍然正确:< / p>

git merge

如果这为您提供了正确的链,请给自己一个更长久的名称,指向提交$ git log ORIG_HEAD 。这可以是分支名称(因此&#34;复活&#34;新名称下的旧分支),或标记名称,或者实际上任何其他名称,但分支和标记很容易:

o4

(你不 这样做,而且随着你对git越来越熟悉,你可以跳过这一步,但在此之前它可能是好事。)


如果$ git branch zombie ORIG_HEAD 遭到重击(例如,通过另一个rebase,或合并,或其他什么),该怎么办?好吧,那就是reflogs。

ORIG_HEAD有一个reflog,默认情况下,每个分支名称都有另一个reflog。在这种情况下,要使用的是HEAD

的reflog
branch

但是您可以使用$ git reflog branch $ git log -g branch 来显示git reflog的一个(这个更吵,这就是为什么只看HEAD可能更好的原因):

branch

在所有输出中的某个地方,您应该能够找到提交$ git reflog $ git log -g 。您可能会发现很多类似o4的其他提交,这就是为什么o4会有所帮助,因为它会让您找到真实的(或正确的)git log -g

无论如何,假设你最终想出了一个reflog风格&#34;相对的名字&#34; (例如o4branch@{1}),您可以找到原始SHA-1或使用该相对名称再次复活branch@{yesterday}的僵尸版本:

branch

或:

$ git branch zombie branch@{yesterday}

或其他什么。


所有这一切都会给你一个名字$ git branch zombie feedd0gf00d ,其中图表的图形中有三个问号。您仍然必须使用它来查找已删除的提交,在本例中为commit zombie。您可以通过原始SHA-1(通过阅读o2)找到它并重新重新绑定并拉入其中,或者选择它以将副本附加到git log或其他任何内容。

如果您要做的只是将n4设置回提交branch,您甚至可以完全免除僵尸分支,只需在分支{{1}上执行o4 }}:

git reset --hard

或:

branch

请注意,$ git checkout branch # if needed $ git reset --hard feedd0gf00d 之后的内容只是任何提交ID。 $ git reset --hard ORIG_HEAD 使reset --hard删除工作树并将其替换为目标提交,而--hard操作本身告诉git:&#34;使当前分支指向提交-ID我即将给你,不管它现在命名的分支 - 提示 - &#34;

换句话说,在reset完成后发现您在制作reset链时遗漏git rebase,如果您立即o2,git会更改此内容:< SUP> 1

n1 - n3 - n4

到此:

git reset --hard ORIG_HEAD

o1 - o2 - o3 - o4 <-- ORIG_HEAD / ..- * - x <-- main \ n1 - n3 - n4 <-- HEAD=branch 提交的 o1 - o2 - o3 - o4 <-- ORIG_HEAD, HEAD=branch / ..- * - x <-- main \ n1 - n3 - n4 [abandoned] 链实际上仍然在回购中,当然:在reflog中有一个指向[abandoned]的名称!

(reflog条目最终会在30到90天之后失效,具体取决于尚未感兴趣的细节 - 一旦过期且没有名称可供查找{{1} }或n或其他,然后 n4将清理并删除它们。)


1 请注意,我已将n4符号添加到此图表中,以指明您所在的分支。这个o4的东西实际上是git跟踪你所依赖的分支的非常好的近似。在git gc目录中,有一个名为HEAD=的文件,该文件只包含当前分支的名称! 2 如果您在其中写入新名称文件,git改变了你在哪个分支上的想法(不改变任何其他内容)。这正是HEAD=的作用:将新名称写入.git。 (使用HEAD添加更多操作:git reset --soft然后更新索引/登台区域;并使用HEAD添加更多:--mixed然后清除工作目录内容用它们放入git reset文件中的任何内容替换它们。)

2 在&#34;分离的HEAD&#34;模式,该文件包含当前提交的原始SHA-1,而不是当前分支的名称。事实上,这是&#34;在分支上的真正区别&#34;并且处于&#34;分离的HEAD&#34;模式。当git想要知道当前提交时,它会查看文件--hard。如果它有原始SHA-1,那就是答案。如果它有一个分支名称,git会读取分支名称以获取原始SHA-1。这是唯一允许的两个设置 - git reset文件中没有任何其他设置。

答案 1 :(得分:1)

git reflog 适合你吗?我认为它应该仍然在垃圾收集器中,除非你运行git gc