git reflog expire和git fsck --unreachable [英] git reflog expire and 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屋!