git fsck 结合 --lost-found 和 --unreachable

git fsck combining --lost-found and --unreachable

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

我从这个 repo 开始:

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

其中 r 是从 a 的分离 HEAD 创建的。 然后我想在 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 版本 return 和 r 提交?为什么在rebase 之前的cd 不显示在无法访问的对象中?我以为我理解阅读链接问题的区别,但我显然遗漏了一些东西。我仍然有完整的 reflog,但我想你不需要它,因为所有提交(与 stash 相关的提交除外)都被引用了。


我知道我应该创建另一个 post 但第二个问题部分相关。我出于好奇试了一下:

$ 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 的联合),这是非常出乎意料的。为什么会这样?

其中有些确实令人费解,似乎没有正确记录,但快速浏览 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 运行 git stash push 产生了一个由两个新提交组成的新存储。 Git 然后像往常一样进行变基,将原始 git log --all --decorate --oneline --graph 输出中可见的 cab074f7b7bac0 提交复制到新的 2e1cb7dcaee68c 提交在第二个输出中可见。

Why does only the --lost-found version return the r commit? And why are not the c and d before the rebase shown among the unreachables?

大概该提交仍在 HEAD reflog 中。这使得它可以从引用访问——但是由于 --lost-found 意味着 --no-reflogs,这次它变得不可访问了。 cd 的原始版本也是如此:它们可以通过多个引用日志条目访问,来自 HEADmaster 的引用日志。

Combining both options I get all the unreachable commits (and not just the union of --lost-found and --unreachable), this is very unexpected. Why does it behave like this?

比较费解 [编辑: 已解决;见下文。] 让我们 运行 按照你的 git fsck 命令的顺序排列这些:

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

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

  • fsck 3:r 和变基提交在 git fsck --unreachable 下仍然不可见,因为它们是从 reflogs 中引用的。但是现在,对于 --lost-found,fsck 不会查看 reflog。我们应该期望看到 autostash w 提交、r 提交和 pre-rebase d,所有这些都是悬而未决的。 [编辑:根据,这是错误的:w链接回i d, 所以这会隐藏 d.]

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

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

  • fsck 4,就在您的第二个问题部分之前:要么 --unreachable 关闭了 pseudoref 包含,要么——我认为这更有可能——你在在触动 ORIG_HEAD 之间,因此它不再选择原始的预变基 d 提交。 [edit] 自 --unreachable 列出所有无法访问的提交,d 可从自动存储 w 提交间接访问的事实无关紧要,我们看到了所有内容。

如果您想报告 Git 文档错误,即 fsck 文档没有注意到 --lost-found 暗示 --no-reflogs,您应该这样做。