Elencare ed eliminare i commit di Git che non si trovano in alcun ramo (penzoloni?)


146

Ho un repository Git con un sacco di commit che non si trovano in un ramo particolare, posso git showfarlo, ma quando provo ad elencare i rami che li contengono, non riporta nulla.

Ho pensato che questo fosse il problema dei commit / tree penzolanti (a causa del ramo -D), quindi ho eliminato il repository, ma dopo ho ancora lo stesso comportamento:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

Nessuna uscita, niente penzoloni (giusto?). Ma il commit esiste

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

e non è raggiungibile attraverso alcun ramo come

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

non dà output.

Qual è esattamente lo stato di quel commit? Come posso elencare tutti i commit in uno stato simile? Come posso eliminare i commit come quelli?


Risposte:


75

Nessuna uscita, niente penzoloni (giusto?)

Si noti che i commit a cui si fa riferimento dal reflog sono considerati raggiungibili.

Qual è esattamente lo stato di quel commit? Come posso elencare tutti i commit con uno stato simile

Passa --no-reflogsper convincere git fscka mostrarteli.

Come posso eliminare i commit come quelli?

Una volta scadute le voci del reflog, anche quegli oggetti verranno ripuliti git gc.

Scadenza è regolato dai gc.pruneexpire, gc.reflogexpiree gc.reflogexpireunreachableimpostazioni. Cf. git help config.

Le impostazioni predefinite sono tutte abbastanza ragionevoli.


2
quindi in pratica stai dicendo che i reflow per commit penzolanti verranno rimossi automaticamente dopo un po '?
morale

2
In sostanza: sì - tranne per il fatto che la domanda è un po 'confusa. Sto dicendo che tutte le voci di reflog vengono rimosse automaticamente dopo un po ', ma è possibile modificarle tramite le impostazioni di configurazione. E poiché un commit viene chiamato penzoloni solo quando non ha nulla che lo indichi - incluse le voci di reflog -, i "reflogs per i commit penzolanti" non sono una cosa. Sarebbero "reflogs per commit irraggiungibili ".
Aristotele Pagaltzis,

"Sarebbero" reflogs per commit irraggiungibili "." Ma hai detto "i commit a cui fa riferimento il tuo reflog sono considerati raggiungibili". Quindi, come possono essere "reflogs per commit irraggiungibili" una cosa? Sono così confuso.
LarsH

1
Sì, non ero coerente. Normalmente le persone non pensano al reflog e quando dicono "irraggiungibile" implica "da un ref". Persino lo git help glossarydefinisce in questo modo ... mentre la sua definizione di "raggiungibile" non è ridotta in quel modo, quindi sono contraddittorie. Divertente - quindi quello che ho detto è in realtà coerente con la confusione in gitglossary... Non sono i concetti che confondono, però, solo la terminologia. Il punto è che i commit "penzoloni" sono quelli che nient'altro indica. Aiuterebbe se dico "reflogs per commit altrimenti irraggiungibili" ...?
Aristotele Pagaltzis,

Questo è tutto molto confuso. Rendiamolo semplice. Quando mastersei sul ramo , lo fai git commite ricevi un commit 000001. Quindi lo fai git commit --amend, il che ti dà impegno 000002. Non ci sono tag o rami che puntano 000001più a, e non puoi vederlo nel tuo registro senza l' --reflogopzione, ma se vuoi, puoi comunque arrivarci git checkout 000001. Ora la domanda è: è 000001un commit penzolante , un commit irraggiungibile , o nessuno dei due, o entrambi?
Chharvey,

264

Per rimuovere tutti i commit sospesi e quelli raggiungibili dai reflog, procedi come segue:

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

Ma assicurati che questo è quello che vuoi. Ti consiglio di leggere le pagine man ma ecco l'essenza:

git gcrimuove oggetti non raggiungibili (commit, alberi, BLOB (file)). Un oggetto non è raggiungibile se non fa parte della storia di un ramo. In realtà è un po 'più complicato:

git gc fa alcune altre cose ma non sono rilevanti qui e non sono pericolose.

Gli oggetti non raggiungibili che durano meno di due settimane non vengono rimossi, quindi usiamo il --prune=nowche significa "rimuovere gli oggetti non raggiungibili che sono stati creati prima d'ora".

Gli oggetti possono anche essere raggiunti tramite il reflog. Mentre i rami registrano la storia di alcuni progetti, i reflog registrano la storia di questi rami. Se modifichi, resetti, ecc. I commit vengono rimossi dalla cronologia delle filiali ma git li tiene in giro nel caso ti accorgessi di aver fatto un errore. I reflog sono un modo conveniente per scoprire quali operazioni distruttive (e altre) sono state eseguite su un ramo (o HEAD), rendendo più semplice annullare un'operazione distruttiva.

Quindi dobbiamo anche rimuovere i reflog per rimuovere effettivamente tutto ciò che non è raggiungibile da un ramo. Lo facciamo scadendo i --allreflog. Anche in questo caso git mantiene un po 'dei reflogs per proteggere gli utenti in modo che ancora una volta si deve dire di non farlo: --expire-unreachable=now.

Dal momento che utilizzo principalmente il reflog per recuperare da operazioni distruttive, di solito utilizzo --expire=nowinvece, il che annulla completamente i reflog.


1
Ti dico quali comandi usare che non è ovvio - non dovrebbe essere sufficiente GC? Se non hai mai usato git-reflog prima non lo saprai. Quindi ora che sai quali comandi devi usare dovresti cercare le opzioni menzionate nelle loro pagine man. Ovviamente potrei invece semplicemente copiare quelle informazioni da lì ...
tarsius

1
Bene, in realtà dico esattamente quello che fa: "rimuovi tutti i commit penzolanti e quelli raggiungibili dai reflog". Se non sai cosa sono i reflog: leggi di nuovo il manuale.
Tarsius

7
Sebbene la risposta fornita possa essere corretta, @ erikb85 ha ragione nel sottolineare che non c'era educazione su ciò che ti veniva detto di fare. Seguire con RTFM è ancora meno utile. Sì, dovremmo tutti leggere tutta la documentazione. In alcuni casi, forse la persona che sta effettuando la ricerca non cerca a fondo la documentazione per sapere cosa sta succedendo. Quindi, un po 'di educazione su ciò che stanno facendo i comandi sarebbe utile per tutti coloro che trovano questa risposta in seguito.
Lee Saferite,

@LeeSaferite spero che ora siate tutti felici :-)
tarsius

12
git reflog expire --expire-unreachable=now --alllascia cadere tutte le tue scorte!
Vsevolod Golovanov,

22

Ho avuto lo stesso problema, ancora dopo aver seguito tutti i consigli in questo thread:

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

Se non è un reflog e non un ramo, ... deve essere un tag !

git tag                             # showed several old tags created before the cleanup

Ho rimosso i tag con git tag -d <tagname>e ripetuto la pulizia e i vecchi commit erano spariti.


C'è già una risposta sui tag ( stackoverflow.com/a/37335660/450127 ) e non sembra che questo aggiunga qualcosa di nuovo. Questo non dovrebbe essere rimosso a favore della risposta precedente?
Ian Dunn,

Anzi, in qualche modo ho trascurato quella risposta. Sebbene 4 persone abbiano trovato utile la mia risposta, quindi forse non è così inutile? Inoltre ho raggruppato tutte le possibilità in una risposta concisa.
jakub.g

1
Anche se duplicata, questa pagina potrebbe apparire in Risultato di Google e aiuta immediatamente le persone con lo stesso problema, meglio di reindirizzare le persone ripetutamente verso collegamenti che potrebbero avere la risposta corretta.
Alexandre T.,

14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

probabilmente deve solo essere

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

riferire anche sui rami dei telecomandi


grazie, ora ho trovato i miei telecomandi / origine / next che contengono ancora questo commit. come rimuoverlo? git push -d origin nextnon aiuta.
iRaS,


grazie - ha git fetch --prunefatto il trucco. ma in tutte le risposte mi manca un controllo per i tag che fanno riferimento a questo commit. Non so ancora come verificare la presenza di tag con un commit (ho rimosso tutto).
iRaS,

Ma ... questo significa che i commit che sono raggiungibili solo da filiali remote (e nessuna filiale locale) sono considerati raggiungibili, e quindi git fsck --unreachablestanno effettivamente comunicando attraverso la rete con il telecomando per scoprire quali commit sono raggiungibili?
LarsH

1
Ha risposto alla mia domanda ... sì, i commit che sono raggiungibili solo da filiali remote (e non da filiali locali) sono considerati raggiungibili; ma git fsck --unreachablenon è necessario comunicare in rete con il telecomando per scoprire quali filiali remote contengono quali commit. Le informazioni sulla filiale remota sono memorizzate localmente, ad es. .git/refs/remotes/origin(O in packed-refs).
LarsH

8

Ho avuto un problema simile. Ho corso git branch --contains <commit>e non ha restituito alcun output proprio come nella domanda.

Ma anche dopo aver corso

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

il mio commit era ancora accessibile tramite git show <commit>. Questo perché uno dei commit nel suo "ramo" distaccato / appeso era stato taggato. Ho rimosso il tag, eseguito di nuovo i comandi sopra ed ero dorato. git show <commit>restituito fatal: bad object <commit>- esattamente ciò di cui avevo bisogno. Spero che questo aiuti qualcun altro che era bloccato come me.


come hai rimosso il tag?
Bapors,

@bapors Elenca tutti i tag, trova quello che fa riferimento al commit in questione, quindi eliminalo. stackoverflow.com/questions/5480258/…
Andrew Larsson,

4

Ho accidentalmente colpito la stessa situazione e ho scoperto che le mie sequenze contengono riferimenti al commit irraggiungibile, e quindi il presunto commit irraggiungibile era raggiungibile dagli stash.

Questo è quello che ho fatto per renderlo davvero irraggiungibile.

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now

2

git gc --prune=<date>per impostazione predefinita, la potatura di oggetti più vecchi di due settimane fa. È possibile impostare una data più recente. Tuttavia, i comandi git che creano oggetti sciolti generalmente eseguiranno git gc --auto (che elimina gli oggetti sciolti se il loro numero supera il valore della variabile di configurazione gc.auto).

Sei sicuro di voler eliminare questi commit? L'impostazione predefinita di gc.auto assicurerà che gli oggetti sciolti non occupino una quantità irragionevole di memoria, e conservare oggetti sciolti per un certo periodo di tempo è generalmente una buona idea. In questo modo, se ti rendi conto domani che il tuo ramo eliminato conteneva un commit di cui avevi bisogno, puoi recuperarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.