我发现了许多关于git fsck
的有趣帖子,所以我想对它们进行一些实验。首先,我在这个问题之前读到的所有来源:
-
如何通过关键字在GIT存储库中找到无法访问的提交哈希?
-
git fsck:悬空与无法到达与丢失有何区别?
-
获取所有git提交的列表,包括丢失的
我从这个回购开始:
* 9c7d1ea (HEAD -> test) f
* cd28884 e
| * 7b7bac0 (master) d
| * cab074f c
|/
* d35af2c b
| * f907f39 r # unreferenced commit
|/
* 81d6675 a
其中CCD_ 2是从从CCD_ 4分离的CCD_。然后我想在test
上重新设置master
的基础,但我有一些未标记的更改,所以我做了:
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
提交?为什么rebase
之前的c
和d
没有显示在不可达的中?我以为我理解阅读相关问题的区别,但我显然遗漏了一些东西。我仍然有完整的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
#...
第二个问题
结合这两个选项,我得到了所有无法访问的提交(而不仅仅是--lost-found
和--unreachable
的并集),这是非常出乎意料的。为什么它会这样?
其中有些确实令人费解,而且似乎没有正确的文档记录,但快速查看内置的/fsck.c可以发现,使用--lost-found
:
- 开启
--full
- 打开CCD_ 18
项目1不是特别有趣,因为--full
现在默认是打开的,但文档确实应该指出r
0禁用--no-full
。第2项解释了其余的大部分内容;我对最后一部分有一个猜测[编辑:其余部分]。
请注意,当您运行时
git checkout master && git rebase --autostash test
这使得Git运行git stash push
,它生成了一个由两个新提交组成的新存储。然后,Git像往常一样进行重基,将原始git log --all --decorate --oneline --graph
输出中可见的cab074f
和7b7bac0
提交复制到第二个输出中可见新的2e1cb7d
和caee68c
提交。
为什么只有
--lost-found
版本返回r
提交?为什么再碱基之前的HEAD
0和d
没有显示在不可达的中?
假定提交仍在HEAD
reflog中。这使得它可以从引用访问——但由于--lost-found
意味着--no-reflogs
,所以这次它变得不可访问。c
和d
的原始文件也是如此:它们可以通过来自HEAD
的reflog和master
的多个reflog条目访问。
结合这两个选项,我得到了所有无法访问的提交(而不仅仅是
--lost-found
和a
0的并集),这是非常出乎意料的。为什么它会这样?
这更令人费解[编辑:已解决;请参阅下文。]让我们按照git fsck
命令的顺序运行这些命令:
-
fsck 1和fsck 2:两者都发现了autostash提交。这是因为
git stash push
将原始refs/stash
复制到存储reflog中,因此refs/stash
可以指向autostashw
(工作树)提交。然后,隐含的git stash apply && git stash drop
(git stash pop
)应用了隐藏并将其丢弃,将stash@{1}
条目移回refs/stash
并删除隐藏reflog。因此,来自autostash的CCD_;悬挂";。它不在refs/stash
中,甚至不在stash
reflog中,因为git stash
(ab)使用该reflog作为";隐藏堆栈";。然而,它确实指向来自autotash的i
提交。然后,第一个fsck打印
6387b70fe14f1ecb90e650faba5270128694613d
并将其称为";悬挂";。这是被丢弃的w
提交。第二个fsck
与--unreachable
一起添加了d8bb677ce0f6602f4ccad46123ee50f2bf6b5819
:相应的被丢弃的i
提交。 -
fsck 3:
r
和重基提交在git fsck --unreachable
下保持不可见,因为它们是从reflog中引用的。但现在,有了--lost-found
,fsck不再关注通货再膨胀。我们应该看到autostashw
提交、r
提交和预重定基d
,所有这些都是悬空的。[编辑:根据注释,这是错误的:w
链接回i
和d
,因此这将隐藏d
。]我们实际上看到了
w
和r
提交,但没有看到d
提交为什么不呢这是我的猜测,但很容易测试您是否仍有设置:当您成功使用git rebase
时,Git会创建或更新名为ORIG_HEAD
的伪引用,以在rebase完成前记住提示提交的哈希ID。请注意,在成功移动一个分支的git reset
之后,以及在可能将分支名称移动一段距离的任何其他操作(例如,快进合并)之后,使用相同的名称来记住ref的上一个值很明显,git fsck
必须考虑所有各种*_HEAD
伪引用作为可达性的起点。这也没有记录在案(这里甚至不完全清楚这是故意的——ref代码最近进行了一些相当大的返工,以支持替代的ref后端) -
fsck 4,就在您的"第二个问题"部分之前:
要么[edit]由于--unreachable
关闭了伪引用包含,要么——我认为这更有可能——您在两者之间做了一些操作,触动了ORIG_HEAD
,使其不再选择原始的预重基准d
提交--unreachable
列出了所有不可访问的提交,因此d
可以从autostashw
提交间接访问这一事实无关紧要,我们可以看到所有内容。
如果您想报告Git文档错误,fsck文档没有注意到--lost-found
意味着--no-reflogs
,那么您应该这样做。