首页 > 解决方案 > git fsck 结合 --lost-found 和 --unreachable

问题描述

我发现了很多关于 的有趣帖子git fsck,所以我想对它们进行一些实验。首先,我在这个问题之前阅读的所有资料:

我从这个 repo 开始:

* 9c7d1ea (HEAD -> test) f
* cd28884 e
| * 7b7bac0 (master) d
| * cab074f c
|/  
* d35af2c b
| * f907f39 r # unreferenced commit
|/
* 81d6675 a

r分离HEADa. 然后我想重新定位mastertest但我有一些未分级的更改,所以我做了:

git rebase --autostash test

获得(我没有显示r,但它仍然存在):

* caee68c (HEAD -> master) d
* 2e1cb7d c
* 9c7d1ea (test) f
* cd28884 e
* d35af2c b
* 81d6675 a

接下来我运行:

$ git fsck
#...
dangling commit 6387b70fe14f1ecb90e650faba5270128694613d # stash
#...
$ git fsck --unreachable
#...
unreachable commit 6387b70fe14f1ecb90e650faba5270128694613d # stash
unreachable commit d8bb677ce0f6602f4ccad46123ee50f2bf6b5819 # stash index
#...
$ git fsck --lost-found
#...
dangling commit 6387b70fe14f1ecb90e650faba5270128694613d # stash
dangling commit f907f39d41763accf6d64f4c736642c0120d5ae2 # r
#...

第一个问题

为什么只有--lost-found版本返回r提交?为什么不可达之间显示cd之前?rebase我以为我理解阅读链接问题的区别,但我显然遗漏了一些东西。我仍然有完整的 reflog,但我猜你不需要它,因为所有提交(除了与 相关的stash)都被引用了。


我知道我应该创建另一个帖子,但第二个问题是部分相关的。我出于好奇尝试了:

$ git fsck --lost-found --unreachable
#...
unreachable commit 6387b70fe14f1ecb90e650faba5270128694613d # stash
unreachable commit d8bb677ce0f6602f4ccad46123ee50f2bf6b5819 # stash index
unreachable commit f907f39d41763accf6d64f4c736642c0120d5ae2 # r
unreachable commit 7b7bac0608936a0bcc29267f68091de3466de1cf # c before rebase
unreachable commit cab074f2c9d63919c3fa59a2dd63ec874b0f0891 # d before rebase
#...

第二个问题

结合这两个选项,我得到了所有无法访问的提交(而不仅仅是 and 的--lost-found并集--unreachable),这是非常出乎意料的。为什么它会这样?

标签: gitgit-rebasegit-stashgit-fsckgit-dangling

解决方案


其中一些确实令人费解,并且似乎没有正确记录,但快速浏览一下builtin/fsck.c表明使用--lost-found

  1. 打开--full
  2. 打开--no-reflogs

第 1 项并不是特别有趣,因为--full无论如何现在默认情况下都是打开的,但文档确实应该指出--lost-found禁用--no-full。第 2 项解释了其余的大部分内容;我对最后一部分有一个猜测[编辑:其余部分]。

请注意,当您运行时:

git checkout master && git rebase --autostash test

这使得 Git run git stash push,它创建了一个包含两个新提交的新存储。然后 Git 像往常一样进行变基,将原始输出中可见的cab074f和提交复制到第二个输出中可见的新和提交。7b7bac0git log --all --decorate --oneline --graph2e1cb7dcaee68c

为什么只有--lost-found版本返回r提交?为什么不可达之间显示变基之前的c和?d

据推测,该提交仍在HEADreflog 中。这使得它可以从引用中访问——但由于--lost-found蕴含--no-reflogs,这一次它变得不可访问。和 的原件也是如此cd它们可以通过多个 reflog 条目访问,来自HEAD's 的 reflog 和master's。

结合这两个选项,我得到了所有无法访问的提交(而不仅仅是 and 的--lost-found并集--unreachable),这是非常出乎意料的。为什么它会这样?

这更令人费解。[编辑:解决;见下文。]让我们按照您的git fsck命令顺序运行它们:

  • fsck 1 和 fsck 2:两者都发现了 autostash 提交。那是因为git stash push将原始文件复制refs/stash到了 stash reflog,所以这refs/stash可能指向 autostash w(工作树)提交。然后隐含的git stash apply && git stash drop( git stash pop) 应用存储并将其删除,将stash@{1}条目移回refs/stash并删除存储引用日志。所以w来自 autostash 的提交确实是“悬空的”。它不在,refs/stash甚至不在stashreflog 中,因为git stash(ab) 将此 reflog 用作“存储堆栈”。但是,它确实指向i来自 autostash 的提交。

    然后,第一个 fsck 打印6387b70fe14f1ecb90e650faba5270128694613d并将其称为“悬空”。那w就是被丢弃的提交。第二个fsck,with --unreachable,添加d8bb677ce0f6602f4ccad46123ee50f2bf6b5819:被删除的相应i提交。

  • fsck 3:r和 rebased 提交仍然不可见,git fsck --unreachable因为它们是从 reflogs 中引用的。但是现在,有了--lost-found, fsck 不会查看 reflog。我们应该期望看到 autostashw提交、r提交和 pre-rebased都悬空。[编辑:根据评论,这是错误的:w链接回i d,所以这将隐藏d。]

    我们实际上看到了wandr提交,但没有看到dcommit为什么不? 这是我的猜测,但很容易测试您是否仍有设置:当您git rebase成功使用时,Git 会创建或更新伪引用,以在 rebase 完成之前ORIG_HEAD记住提示提交的哈希 ID 。请注意,在成功移动一个之后,以及在任何其他可能将分支名称移动一段距离的操作(例如,快进合并)之后,这个相同的名称用于记住 ref 的先前值。git reset

    很明显,git fsck必须将所有各种*_HEAD伪引用作为可达性的起点。这也没有记录在案(甚至不完全清楚这是故意的——参考代码最近进行了一些相当繁重的修改,以支持替代参考后端)。

  • fsck 4,就在你的第二个问题部分之前:要么--unreachable关闭伪引用包含,或者——我认为这更有可能——你在这两者之间做了一些事情,ORIG_HEAD这样它就不再选择原始的预变基d提交。[编辑] 由于--unreachable列出了所有无法访问的提交,因此d可以从自动存储提交间接访问的事实w是无关紧要的,我们可以看到所有内容。

如果您想报告一个 Git 文档错误,而 fsck 文档没有指出这--lost-found意味着--no-reflogs,您应该这样做。


推荐阅读