git reflog expire和git fsck --unreachable [英] git reflog expire and git fsck --unreachable

查看:159
本文介绍了git reflog expire和git fsck --unreachable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:这个问题纯粹是信息性的,并不代表我遇到的实际问题。我只是想弄清楚一些东西(因为我喜欢搞定东西,而且我知道你也是)。



所以我在玩git,试图到期修改提交。我的reflog如下所示:

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

这意味着我做了两个提交( da8534a 5a1e68a ),然后我用 4eea1cd 修改第三个提交 ff576c1 p>

正如所料,我的 git log 看起来像这样:

  * 4eea1cd(HEAD,master)修改提交
* 5a1e68a测试:foo
* da8534a初始提交

从我(虽然我)知道提交的有效性,某天(很可能,默认30天) git gc 应该收集 ff576c1 。现在我不想等待30天才能看到这种情况,所以我开始运行一些命令,首先:

  git fsck --unreachable --no-reflogs 

正如预期的那样,给了我:

 无法访问的blob 5716ca5987cbf97d6bb54920bea6adde242d87e6 
无法访问的树1e60e555e3500075d00085e4c1720030e077b6c8
无法访问的提交ff576c1b4b6df57ba1c20afabd718c93dacf2fc6

所有相信我将会过期那可怜的寂寞 ff576c1 commit,然后我运行 git reflog expire

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

哪一次,那次给了我:

 会修剪提交:test:bar 
将修剪提交(修改):修改提交

起初我虽然我的 HEAD 没有引用 master code>,但你可以看到在我之前给出的 git log 输出中,实际上是这样。此外, cat .git / HEAD 确认(yelding ref:refs / heads / master )。无论如何,尽管这很愚蠢,因为 4eea1cd 是我的 master 分支的头。



所以我在这里,都感到困惑,这两个命令不会给我同样的提交,并且想知道怎么可能 4eea1cd 可能无法访问,因为它是我的 master 分支的实际提示。



关于发生了什么的任何想法?



编辑:我刚才注意到,如果我将 - 重写选项添加到 git reflog expire ,就像这样: -expire -reachable = now --all --rewrite

然后我只得到修正的提交:

 会剪掉提交:test:bar 

我仍然不明白,因为根据 git help reflog

  --rewrite 
当到期或删除时,调整每个reflog条目以确保在
旧的sha1字段指向前
条目的新sha1字段。

对我来说这没有意义。至少我不明白,因为它明显地改变了一些东西。

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



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



就你而言,最新的reflog条目的预更改值指的是无法访问的提交。即使由更改后的值标识的提交仍然可以访问, reflog expire 会删除条目。



设计很容易实现,并导致日志不完整但准确。



- 重写选项



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


  • 在日志中留下缺口
  • 有关可继续提交的提交的有用信息丢失



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




  • 与前面一样,删除了由更改后的SHA1标识的提交不可用的条目。

  • 更改提交无法访问的条目被修改为桥接被删除的条目留下的空缺(更改前的SHA1被设置为先前条目的更改后的SHA1的值)。


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


Disclaimer: this question is purely informational and does not represent an actual problem I'm experiencing. I'm just trying to figure out stuff for the sake of it (because I love figuring stuff out, and I know you do too).

So I was playing with git, trying to expire an amended commit. My reflog looks like that:

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

Which means I made two commits (da8534a and 5a1e68a), then a third commit ff576c1 that I amended with 4eea1cd.

Just as expected, my git log looks something like that:

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

From what I (though I) know about expirability of commits, some day (most likely, in 30 days by default) git gc should collect ff576c1. Now I don't want to wait for 30 days to see that happen, so I start running a few commands, first:

git fsck --unreachable --no-reflogs

Which, just as expected again, gives me:

unreachable blob 5716ca5987cbf97d6bb54920bea6adde242d87e6
unreachable tree 1e60e555e3500075d00085e4c1720030e077b6c8
unreachable commit ff576c1b4b6df57ba1c20afabd718c93dacf2fc6

All confident that I'm going to expire that poor lonely ff576c1 commit, I then run git reflog expire:

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

Which, that time, gives me:

would prune commit: test: bar
would prune commit (amend): amend commit

At first I though my HEAD was not referencing master, but as you can see in the git log output I gave earlier, it actually does. Also, cat .git/HEAD confirms that (yelding ref: refs/heads/master). Anyway, even that though was silly, since 4eea1cd is the head of my master branch.

So here I am, all confused that these two commands won't give me the same commits, and wondering how the hell could 4eea1cd possibly be unreachable, since it's the actual tip of my master branch.

Any idea on what's going on?

EDIT: I just noticed if I add the --rewrite option to git reflog expire, like that:

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

Then I only get the amended commit:

would prune commit: test: bar

I still don't understand, because according to 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.

Which doesn't make sense in my case. Well at least I don't get it, since obvisouly it does change something.

解决方案

This behavior comes from an interaction between the reflog design philosophy and the requirements of garbage collection.

For a commit to be safely deleted by the garbage collector, all references to that commit must be deleted—including references in reflog entries. Despite the appearance of reflog show, each reflog entry actually contains two SHA1 identifiers: the value of the ref before the change and the value of the ref after the change. To ensure safe garbage collection, reflog expire simply deletes any entry where one of the two SHA1s identifies an unreachable commit.

In your case, the pre-change value of the most recent reflog entry refers to an unreachable commit. Even though the commit identified by the post-change value is still reachable, reflog expire deletes the entry.

This design is simple to implement and results in an incomplete but accurate log.

the --rewrite option

Unfortunately, deleting an entry that refers to a still-reachable commit has a couple of problems:

  • a gap is left in the log
  • useful information related to still-reachable commits is lost

The --rewrite option addresses these problems by changing the behavior in the following way:

  • As before, entries where the commit identified by the post-change SHA1 is unreachable are deleted.
  • Entries where the pre-change commit is unreachable are modified to bridge the gap left by the deleted entry (the pre-change SHA1 is set to the value of the previous entry's post-change SHA1).

Unfortunately, modifying the entry results in a log that no longer accurately reflects the history of the ref. For example, the change reason may no longer make sense after the rewrite. This is why --rewrite is not the default.

这篇关于git reflog expire和git fsck --unreachable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆