git reflog expire and git fsck --unreachable

20

声明:这个问题纯粹是信息性的,不代表我正在经历实际问题。我只是试图为了解决问题而进行尝试(因为我喜欢解决问题,我知道你也是一样)。

所以我在使用Git时,试图使修改后的提交过期。我的引用日志看起来像这样:

4eea1cd HEAD@{0}: commit (amend): amend commit
ff576c1 HEAD@{1}: commit: test: bar
5a1e68a HEAD@{2}: commit: test: foo
da8534a HEAD@{3}: commit (initial): initial commit

这意味着我做了两个提交(da8534a5a1e68a),然后进行了第三个提交ff576c1,并使用4eea1cd进行了修改。

正如所预期的那样,我的git log大致如下:

* 4eea1cd (HEAD, master) amend commit
* 5a1e68a test: foo
* da8534a initial commit

根据我所知道的提交到期处理,某一天(默认情况下最可能在30天内),git gc应该会清除ff576c1. 现在我不想等30天才能看到它被清除,因此我开始运行一些命令,首先是:

git fsck --unreachable --no-reflogs

正如预期的一样,这又给了我:

unreachable blob 5716ca5987cbf97d6bb54920bea6adde242d87e6
unreachable tree 1e60e555e3500075d00085e4c1720030e077b6c8
unreachable commit ff576c1b4b6df57ba1c20afabd718c93dacf2fc6

我非常有把握要废弃这个可怜而孤独的ff576c1提交,然后我运行了git reflog expire

git reflog expire --dry-run --expire-unreachable=now --all

当时给了我:

would prune commit: test: bar
would prune commit (amend): amend commit
起初我以为我的HEAD没有引用master,但是正如你可以在我之前给出的git log输出中看到的那样,它实际上是引用了的。另外,cat .git/HEAD也证实了这一点(产生ref: refs/heads/master)。无论如何,即使那个想法很愚蠢,因为4eea1cd是我的master分支的最新提交。
所以,我很困惑这两个命令为什么不能给出相同的提交,并想知道为什么4eea1cd可能无法到达,因为它是我的master分支的实际最新提交。
你有任何想法吗?
编辑:我刚刚注意到,如果我将--rewrite选项添加到git reflog expire中,就像这样:
git reflog expire --dry-run --expire-unreachable=now --all --rewrite

那么我只得到了修改后的提交:

would prune commit: test: bar

我还是不明白,因为根据 git help reflog:

   --rewrite
       While expiring or deleting, adjust each reflog entry to ensure that
       the old sha1 field points to the new sha1 field of the previous
       entry.

在我的情况下这是没有意义的,至少我不明白,因为显然它确实改变了一些东西。

1个回答

13

这种行为源于reflog设计哲学和垃圾收集的要求之间的交互。

为了使提交能够被垃圾回收器安全删除,必须删除对该提交的所有引用,包括reflog条目中的引用。尽管看起来像是reflog show,但实际上每个reflog条目都包含两个SHA1标识符:更改前引用的值和更改后引用的值。为确保安全的垃圾回收,reflog expire只需删除其中一个SHA1标识不可达提交的任何条目。

在您的情况下,最新的reflog条目的更改前值引用一个不可达的提交。即使由更改后值标识的提交仍然可达,reflog expire也会删除该条目。

此设计易于实现,并产生不完整但准确的日志。

--rewrite选项

不幸的是,删除引用仍然可达的提交的条目有一些问题:

  • 日志中留下了一个空白
  • 与仍然可达的提交相关的有用信息丢失了

--rewrite选项通过以下方式更改行为来解决这些问题:

  • 与以前一样,删除标识由更改后SHA1的提交不可达的条目。
  • 修改标识更改前的提交不可达的条目,以填补删除的条目留下的空白(将更改前SHA1设置为上一个条目的更改后SHA1的值)。

不幸的是,修改条目会导致日志不再准确地反映引用的历史。例如,在重写之后,更改原因可能不再有意义。这就是为什么--rewrite不是默认选项的原因。


哦,我明白了,现在一切都有意义了。谢谢。 - Geoffrey Bachelet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接