git reflog expire和git fsck --unreachable

时间:2012-02-07 21:36:41

标签: git reachability reflog

免责声明:此问题仅供参考,并不代表我遇到的实际问题。我只是想弄清楚它的东西(因为我喜欢弄清楚东西,我知道你也是这样)。

所以我正在玩git,试图使修改后的提交失效。我的reflog看起来像这样:

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确认了(yelding ref: refs/heads/master)。无论如何,即使这是愚蠢的,因为4eea1cdmaster分支的负责人。

所以在这里,我很困惑,这两个命令不会给我相同的提交,并想知道地狱怎么可能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.

在我的案例中没有意义。好吧,至少我没有得到它,因为obvisouly它确实改变了一些东西。

1 个答案:

答案 0 :(得分:11)

此行为来自reflog设计理念与垃圾收集要求之间的交互。

对于垃圾收集器安全删除的提交,必须删除对该提交的所有引用 - 包括reflog条目中的引用。尽管出现reflog show,但每个reflog条目实际上包含两个SHA1标识符:更改前的ref值和更改后的ref值。为了确保安全的垃圾收集,reflog expire只删除两个SHA1中的一个标识无法访问的提交的任何条目。

在您的情况下,最近的reflog条目的更改前值是指无法访问的提交。即使由更改后的值标识的提交仍可访问,reflog expire也会删除该条目。

此设计易于实施,导致日志不完整但准确。

--rewrite选项

不幸的是,删除引用仍然可以访问的提交的条目有几个问题:

  • 日志中留有间隙
  • 与仍无法访问的提交相关的有用信息丢失

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

  • 与以前一样,删除更改后SHA1识别的提交无法访问的条目。
  • 修改了无法访问更改前提交的条目以桥接已删除条目留下的间隙(更改前的SHA1设置为上一个条目的更改后SHA1的值)。

不幸的是,修改条目会导致日志不再准确反映ref的历史记录。例如,重写后更改原因可能不再有意义。这就是--rewrite不是默认值的原因。