Перечисление и удаление коммитов Git, которые не находятся ни в одной ветке (висячие?) - PullRequest
127 голосов
/ 22 сентября 2010

У меня есть Git-репозиторий с множеством коммитов, которые не относятся ни к одной конкретной ветке, я могу git show их, но когда я пытаюсь перечислить ветви, содержащие их, он ничего не сообщает.

Я думал, что это проблема с коммитом / коммитом дерева (в результате ветки -D), поэтому я обрезал репо, но после этого все еще вижу то же поведение:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

Нет вывода, ничего не болтается (верно?). Но коммит существует

$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...

и он не доступен через любую ветку как

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

не выводит.

Каково состояние этого коммита? Как я могу перечислить все коммиты в похожем состоянии? Как я могу удалить такие коммиты?

Ответы [ 7 ]

234 голосов
/ 25 декабря 2010

Чтобы удалить все висячие коммиты и те, которые доступны из повторных журналов, сделайте следующее:

git reflog expire --expire-unreachable=now --all
git gc --prune=now

Но будьте уверены, что это то, что вы хотите. Я рекомендую вам прочитать справочные страницы, но вот суть:

git gc удаляет недоступные объекты (коммиты, деревья, BLOB-объекты (файлы)). Объект недоступен, если он не является частью истории какой-либо ветви. На самом деле все немного сложнее:

git gc делает некоторые другие вещи, но они здесь не актуальны и не опасны.

Недоступные объекты младше двух недель не удаляются, поэтому мы используем --prune=now, что означает «удалить недоступные объекты, созданные ранее».

Объекты также могут быть достигнуты через reflog. В то время как ветки записывают историю какого-либо проекта, повторные журналы записывают историю этих веток. Если вы исправляете, сбрасываете и т. Д. Коммиты удаляются из истории веток, но git сохраняет их на случай, если вы поймете, что допустили ошибку. Reflogs - это удобный способ выяснить, какие деструктивные (и другие) операции выполнялись на ветке (или HEAD), что упрощает отмену деструктивной операции.

Таким образом, мы также должны удалить reflogs, чтобы фактически удалить все, что недоступно из ветви. Мы делаем это путем истечения срока действия --all reflogs. Опять же, git хранит немного reflogs для защиты пользователей, поэтому мы снова должны сказать ему не делать этого: --expire-unreachable=now.

Так как я в основном использую reflog для восстановления после деструктивных операций, я обычно использую --expire=now, что полностью сбрасывает reflogs.

70 голосов
/ 22 сентября 2010

Нет вывода, ничего не болтается (верно?)

Обратите внимание, что коммиты, указанные в вашем журнале, считаются достижимыми.

Каково состояние этого коммита? Как я могу перечислить все коммиты с похожим состоянием

Передайте --no-reflogs, чтобы убедить git fsck показать их вам.

Как я могу удалить такие коммиты?

После истечения срока действия записей reflog эти объекты также будут очищены с помощью git gc.

Срок действия регулируется настройками gc.pruneexpire, gc.reflogexpire и gc.reflogexpireunreachable. Ср git help config.

Все значения по умолчанию вполне разумны.

14 голосов
/ 09 марта 2017

У меня была та же проблема, но после того, как я следовал всем советам в этой теме:

git reflog expire --expire-unreachable=now --all
git gc --prune=now
git fsck --unreachable --no-reflogs   # no output
git branch -a --contains <commit>     # no output
git show <commit>                     # still shows up

Если это не рефлог и не ветка, ... это должен быть тег !

git tag                             # showed several old tags created before the cleanup

Я удалил теги с помощью git tag -d <tagname> и повторил очистку, и старые коммиты исчезли.

13 голосов
/ 13 марта 2011
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

вероятно, просто нужно быть

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

, чтобы также сообщать о филиалах с удаленных компьютеров

5 голосов
/ 20 мая 2016

У меня была похожая проблема. Я запустил git branch --contains <commit>, и он не дал никакого вывода, как в вопросе.

Но даже после запуска

git reflog expire --expire-unreachable=now --all
git gc --prune=now

мой коммит все еще был доступен с помощью git show <commit>. Это было связано с тем, что один из коммитов в его изолированной "ветке" был помечен. Я удалил тег, снова выполнил вышеупомянутые команды, и я был золотой. git show <commit> вернул fatal: bad object <commit> - именно то, что мне было нужно. Надеюсь, это поможет кому-то, кто застрял так же, как и я.

2 голосов
/ 22 сентября 2017

Я случайно попал в ту же ситуацию и обнаружил, что в моих тайниках содержится ссылка на недостижимый коммит, и поэтому предполагаемый недостижимый коммит был доступен из тайников.

Это было то, что я сделал, чтобы сделать его действительно недоступным.

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now
2 голосов
/ 22 сентября 2010

git gc --prune=<date> по умолчанию сокращает объекты старше двух недель назад. Вы можете установить более позднюю дату. Но команды git, которые создают свободные объекты, обычно запускают git gc --auto (который удаляет свободные объекты, если их число превышает значение переменной конфигурации gc.auto).

Вы уверены, что хотите удалить эти коммиты? Установка по умолчанию в gc.auto гарантирует, что незакрепленные объекты не займут неоправданное количество памяти, и, как правило, хорошая идея - хранить незакрепленные объекты в течение некоторого времени. Таким образом, если завтра вы поймете, что ваша удаленная ветка содержит необходимый вам коммит, вы можете восстановить его.

...