Перечисление и удаление коммитов Git, которые не находятся ни в одной ветке (висячие?)
У меня есть Git-репозиторий с множеством коммитов, которые не относятся ни к одной конкретной ветке, я могу git show
их, но когда я пытаюсь перечислить ветви, которые содержат их, он ничего не сообщает.
Я подумал, что это проблема с коммитом / деревом (в результате ветки -D), поэтому я сократил репозиторий, но после этого все еще вижу то же поведение:
$ git fetch origin
$ git fsck --unreachable
$ git fsck
Нет выхода, ничего не болтается (верно?). Но коммит существует
$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...
и он не доступен через любую ветку
$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042
не дает вывода.
Каково именно состояние этого коммита? Как я могу перечислить все коммиты в похожем состоянии? Как я могу удалить такие коммиты?
11 ответов
Нет выхода, ничего не болтается (верно?)
Обратите внимание, что коммиты, на которые ссылается ваш рефлог, считаются достижимыми.
Каково именно состояние этого коммита? Как я могу перечислить все коммиты с похожим состоянием
Проходить --no-reflogs
убедить git fsck
чтобы показать их вам.
Как я могу удалить такие коммиты?
После истечения срока действия записей reflog эти объекты также будут очищены git gc
,
Срок действия регулируется gc.pruneexpire
, gc.reflogexpire
, а также gc.reflogexpireunreachable
Настройки. Ср git help config
,
Все значения по умолчанию вполне разумны.
Чтобы удалить все висячие коммиты и те, которые доступны из reflogs, сделайте это:
git reflog expire --expire-unreachable=now --all
git gc --prune=now
Но будьте уверены, что это то, что вы хотите. Я рекомендую вам прочитать страницы руководства, но вот суть:
git gc
удаляет недоступные объекты (коммиты, деревья, капли (файлы)). Объект недоступен, если он не является частью истории какой-либо ветви. На самом деле это немного сложнее:
git gc
делает некоторые другие вещи, но они здесь не актуальны и не опасны.
Недоступные объекты младше двух недель не удаляются, поэтому мы используем --prune=now
что означает "удалить недоступные объекты, которые были созданы до сих пор".
Объекты также могут быть достигнуты через reflog. В то время как ветки записывают историю какого-либо проекта, повторные журналы записывают историю этих веток. Если вы исправляете, сбрасываете и т. Д. Коммиты удаляются из истории веток, но git сохраняет их на случай, если вы поймете, что допустили ошибку. Reflogs - это удобный способ выяснить, какие деструктивные (и другие) операции выполнялись на ветке (или HEAD), что упрощает отмену деструктивной операции.
Поэтому мы также должны удалить reflogs, чтобы фактически удалить все, что недоступно из ветки. Мы делаем это по истечении срока --all
reflogs. Опять же, git хранит немного reflogs, чтобы защитить пользователей, поэтому мы снова должны сказать ему не делать этого: --expire-unreachable=now
,
Так как я в основном использую reflog для восстановления после деструктивных операций, я обычно использую --expire=now
вместо этого, что убивает reflogs полностью.
У меня была та же проблема, но после того, как я следовал всем советам в этой теме:
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>
и переделал уборку, и старые коммиты исчезли.
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042
наверное просто нужно быть
git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042
также сообщать о филиалах с удаленных
Я случайно попал в ту же ситуацию и обнаружил, что мои тайники содержат ссылку на недостижимый коммит, и, следовательно, предполагаемый недостижимый коммит был доступен из тайников.
Это было то, что я сделал, чтобы сделать его действительно недоступным.
git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now
У меня была похожая проблема. Я побежал 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>
- именно то, что мне было нужно. Надеюсь, это поможет кому-то, кто застрял так же, как и я.
TL:ДР;
На фиксацию можно ссылаться:
- отделение
- тег (!)
- тайник (!!)
- "заменить" (!!?)
Используйте эту команду, чтобы узнать, что это такое:
Длинная версия
я сделалfilter-repo
репо, чтобы очистить некоторые огромные файлы.
Очевидно, закончились некоторые оборванные коммиты. Я пробовал все ответы, и ничего не работало.
git branch --contains <SHA> # nothing!
git fsck --unreachable --no-reflogs # nothing!
Затем я нашел и удалил тег благодаря этому ответу
Тем не менее фиксация продолжала появляться!
Итак, я наконец выполнил эту команду:
git for-each-ref --contains <SHA>
И он вернулся
1b9bf9c63209a4728b2d3dc7946da836dc331bbd commit refs/stash
Бинго!
я побежалgit stash
- и он был (как ни странно) пуст. Как тайник может ссылаться на фиксацию, если в моем тайнике ничего нет?
Однако работаетgit stash drop
несколько раз удалял некоторые богом забытые «висячие» тайники, основанные на устаревших ветках, и, наконец , я смогgc
этот коммит, и мой репозиторий мгновенно стал на 15 МБ меньше.
Более длинная версия
(от другого пользователя) Я использовал приведенные выше советы, но он вернулся
4c79e05b102c673e1f60242c022ff06e017b9a1d commit refs/replace/2acd8f182e14d7d367b3469fd8751f2d9738a884
что за хрень заменить?
git replace -h
показывает, что вы можете перечислить удаление и редактирование этих «замещающих объектов». Странный. Бегgit replace -d 2acd8f
удаляет фиксацию!
git gc --prune=<date>
по умолчанию удаляет объекты старше двух недель назад. Вы можете установить более позднюю дату. Но команды git, которые создают свободные объекты, обычно запускают git gc --auto (который удаляет свободные объекты, если их число превышает значение переменной конфигурации gc.auto).
Вы уверены, что хотите удалить эти коммиты? Установка по умолчанию в gc.auto гарантирует, что незакрепленные объекты не займут неоправданное количество памяти, и, как правило, хорошая идея - хранить незакрепленные объекты в течение некоторого времени. Таким образом, если завтра вы поймете, что ваша удаленная ветка содержит необходимый вам коммит, вы можете восстановить его.
Сначала вам следует подумать, действительно ли вам нужно агрессивно удалять/обрезать все недоступные коммиты.
Возможно, вы получили это сообщение :
warning: There are too many unreachable loose objects; run 'git prune' to remove them.
А потом
Проблема с обрезкой последних коммитов заключается в том, что в худшем случае (я не знаю, насколько это вероятно) вы можете испортить что-то, если вы также взаимодействуете с удаленным компьютером.
Нетипичное поведение возникает, когда в ходе этого процесса репозиторий получает один или несколько запросов. Основная проблема заключается в том, что сервер рекламирует свои объекты в другой момент времени, чем обработка объектов, отправленных клиентом на основе этого объявления.
[…] Если один из этих толчков произойдет раньше
фактически удален, то репозиторий может оказаться в поврежденном состоянии.
Так что, если ваша прямая проблема — «свободные объекты», а не нехватка дискового пространства или слишком много объектов, — тогда вы можете включить эту экспериментальную функцию (начиная с Git 2.40.1):
git config gc.cruftPacks true
Сейчас
В целом кажется безопаснее использовать экспериментальную функцию, если это означает, что у вас не будет причин удалять совсем недавно недоступные объекты.
Если тайник действительно является тайником, которого «не существует», а не тегом,
git fsck --full
может помочь. Это сработало для меня, когда не помогло никакое другое решение.
(Git: Удалить сломанный тайник описывает мою проблему более точно, чем эта ветка)
Я знаю, что это не очень хорошее решение, но я сделалfilter-branch
и в итоге получил дублирующиеся недостижимые коммиты, которые не принадлежали ни к одной ветке, но не могли удалить их автоматически, я перепробовал каждое решение, опубликованное здесь, абсолютно ничего не сработало. Поэтому я перешел в удаленный репозиторий (github), удалил свой локальный репозиторий, а затем снова вытащил и избавился от всех этих недоступных коммитов.