Перечисление и удаление коммитов Git, которые не находятся ни в одной ветке (висячие?)

146

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

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

$ git fetch origin

$ git fsck --unreachable
$ git fsck

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

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

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

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

не дает вывода.

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

Самер Буна
источник
Возможный дубликат Как удалить ошибочные коммиты слияния?
Джош Ли

Ответы:

75

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

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

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

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

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

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

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

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

Аристотель Пагальтзис
источник
2
так вы в основном говорите, что перефразы для свисающих коммитов будут удалены через некоторое время автоматически?
MoralCode
2
В основном: да - за исключением того, что вопрос немного запутан. Я говорю, что все записи reflog удаляются автоматически через некоторое время, но вы можете изменить это через настройки конфигурации. И потому, что совершает только называется оборванным , когда он не имеет ничего указывающего на него - в то числе reflog записей - «reflogs для оборванных совершающих» не вещь. Это были бы «повторные журналы для недостижимых коммитов ».
Аристотель Пагальцис
«Они будут« повторными флагами для недоступных коммитов ». Но вы сказали, что «коммиты, упомянутые в вашем журнале, считаются достижимыми». Так как же могут быть «повторные журналы для недостижимых коммитов»? Я весьма озадачен.
LarsH
1
Да, я не был последовательным. Обычно люди не думают об reflog, и когда они говорят «недостижимый», это подразумевает «с ref». Даже git help glossaryопределяет это таким образом… тогда как его определение «достижимого» не сужается таким образом, поэтому они противоречивы. Забавно - так что то, что я сказал, на самом деле согласуется с путаницей в gitglossary… Хотя не концепции сбивают с толку, а только терминология. Дело в том, что «висячие» коммиты - это те, на которые больше ничего не указывает. Будет ли это поможет , если я скажу «reflogs для иначе недостижимых фиксаций» ...?
Аристотель Пагальцис
Это все очень запутанно. Давайте сделаем это просто. На ветке masterвы делаете git commit, и получаете коммит 000001. Тогда вы делаете git commit --amend, что дает вам совершить 000002. Больше нет меток или веток, указывающих на них 000001, и вы не сможете увидеть их в своем журнале без этой --reflogопции, но при желании вы все равно можете получить к ней доступ git checkout 000001. Теперь вопрос в том, оборванных совершить, или недостижим совершить, или ни, или оба? 000001
Чарви
264

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

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

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

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

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

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

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

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

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

Tarsius
источник
1
Я говорю вам, какие команды использовать, что не очевидно - не должно ли быть достаточно gc? Если вы никогда не использовали git-reflog, вы об этом не узнаете. Итак, теперь, когда вы знаете, какие команды вам нужно использовать, вы должны найти упомянутые опции на их страницах руководства. Конечно, вместо этого я мог бы просто скопировать эту информацию оттуда ...
tarsius
1
Ну, на самом деле я говорю именно то, что он делает: «удалите все свисающие коммиты и те, которые достижимы из reflogs». Если вы не знаете, что такое reflogs: снова прочитайте руководство.
tarsius
7
Хотя приведенный ответ может быть правильным, @ erikb85 правильно указывает, что не было никакого образования о том, что вам велели делать. Отслеживание RTFM еще менее полезно. Да, мы все должны прочитать всю документацию. В некоторых случаях, возможно, человек, выполняющий поиск, не хватает документации достаточно, чтобы знать, что происходит. Таким образом, небольшая информация о том, что делают команды, будет полезна для всех, кто найдет этот ответ позже.
Ли Саферите
@LeeSaferite надеюсь, что вы все счастливы сейчас :-)
tarsius
12
git reflog expire --expire-unreachable=now --allсбрасывает все ваши тайники!
Всеволод Голованов
22

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

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>и повторил очистку, и старые коммиты исчезли.

jakub.g
источник
Там уже есть ответ о тегах ( stackoverflow.com/a/37335660/450127 ), и, похоже, это не добавляет ничего нового. Разве это не должно быть удалено в пользу более раннего ответа?
Ян Данн
Действительно, как-то я упустил этот ответ. Хотя 4 человека нашли мой ответ полезным, так что, может быть, он не так бесполезен? Также я сгруппировал все возможности в один краткий ответ.
jakub.g
1
Даже в случае дублирования эта страница может отображаться в Google Result, и она сразу же помогает людям с той же проблемой, а не просто перенаправляет людей снова и снова на ссылки, которые могут иметь правильный ответ.
Александр Т.
14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

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

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

также сообщать о филиалах с удаленных

sehe
источник
спасибо, теперь я нашел свои пульты / origin / next, которые все еще содержат этот коммит. как это убрать? git push -d origin nextне помогает
iRaS
спасибо - git fetch --pruneсделал свое дело. но во всех ответах мне не хватает проверки для тегов, которые ссылаются на этот коммит. Я до сих пор не знаю, как проверить теги с фиксацией (я удалил все).
IRAS
Но ... означает ли это, что коммиты, которые достижимы только из удаленных ветвей (и без локальных ветвей), считаются достижимыми и, следовательно git fsck --unreachable, фактически связываются по сети с удаленным, чтобы выяснить, какие коммиты достижимы?
LarsH
1
Ответил на мой собственный вопрос ... да, коммиты, которые достижимы только из удаленных ветвей (и без локальных ветвей), считаются достижимыми; но git fsck --unreachableему не нужно связываться по сети с удаленным, чтобы узнать, какие удаленные ветви содержат какие коммиты. Информация об удаленной ветви хранится локально, например, в .git/refs/remotes/origin(или в packed-refs).
LarsH
8

У меня была похожая проблема. Я побежал 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>- именно то, что мне было нужно. Надеюсь, это поможет кому-то, кто застрял так же, как и я.

Эндрю Ларссон
источник
как вы удалили тег?
бары
@bapors Перечислите все теги, найдите тот, который ссылается на данный коммит, а затем удалите его. stackoverflow.com/questions/5480258/…
Эндрю Ларссон
4

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

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

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now
Лэй Чжао
источник
2

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

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

dublev
источник