git reflog expire и git fsck - недоступны
Отказ от ответственности: этот вопрос носит исключительно информационный характер и не представляет реальной проблемы, с которой я сталкиваюсь. Я просто пытаюсь что-то выяснить ради этого (потому что мне нравится разбираться с вещами, и я знаю, что вы тоже).
Поэтому я играл с 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
Что означает, что я сделал два коммита (da8534a
а также 5a1e68a
), то третий коммит 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 ответ
Это происходит из-за взаимодействия философии проектирования reflog и требований сборки мусора.
Чтобы коммит был безопасно удален коммитом, все ссылки на этот коммит должны быть удалены, включая ссылки в записях reflog. Несмотря на появление reflog show
каждая запись reflog содержит два идентификатора SHA1: значение ref до изменения и значение ref после изменения. Для обеспечения безопасного сбора мусора, reflog expire
просто удаляет любую запись, где один из двух SHA1 идентифицирует недоступный коммит.
В вашем случае значение перед изменением самой последней записи reflog относится к недостижимой фиксации. Даже если фиксация, определяемая значением после изменения, все еще достижима, reflog expire
удаляет запись.
Этот проект прост в реализации и приводит к неполному, но точному журналу.
--rewrite
вариант
К сожалению, удаление записи, которая ссылается на все еще достижимый коммит, имеет несколько проблем:
- в журнале остается пробел
- полезная информация, связанная с доступными коммитами, теряется
--rewrite
Опция решает эти проблемы, изменяя поведение следующим образом:
- Как и прежде, записи, в которых фиксация, определенная SHA1 после изменения, недоступна, удаляется.
- Записи, в которых фиксация перед изменением недоступна, модифицируются так, чтобы заполнить пробел, оставленный удаленной записью (SHA1 перед изменением устанавливается равным значению SHA1 после изменения предыдущей записи).
К сожалению, изменение записи приводит к журналу, который больше не точно отражает историю ссылки. Например, причина изменения может больше не иметь смысла после перезаписи. Вот почему --rewrite
не по умолчанию.