Auflisten und Löschen von Git-Commits, die sich nicht in einem Zweig befinden (baumeln?)

146

Ich habe ein Git-Repository mit vielen Commits, die sich unter keinem bestimmten Zweig befinden. Ich kann git showsie, aber wenn ich versuche, Zweige aufzulisten, die sie enthalten, wird nichts zurückgemeldet.

Ich dachte, dies ist das Problem mit den baumelnden Commits / Bäumen (als Ergebnis der Verzweigung -D), also habe ich das Repo beschnitten, aber danach sehe ich immer noch dasselbe Verhalten:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

Keine Ausgabe, nichts baumelt (richtig?). Aber das Commit existiert

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

und es ist über keinen Zweig als erreichbar

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

gibt keine Ausgabe.

Wie genau ist der Stand dieses Commits? Wie kann ich alle Commits in einem ähnlichen Status auflisten? Wie kann ich solche Commits löschen?

Samer Buna
quelle

Antworten:

75

Keine Ausgabe, nichts baumelt (richtig?)

Beachten Sie, dass Commits, auf die in Ihrem Reflog verwiesen wird, als erreichbar gelten.

Wie genau ist der Stand dieses Commits? Wie kann ich alle Commits mit ähnlichem Status auflisten?

Pass, um --no-reflogszu überzeugen git fsck, sie dir zu zeigen.

Wie kann ich solche Commits löschen?

Sobald Ihre Reflog-Einträge abgelaufen sind, werden diese Objekte auch von bereinigt git gc.

Ablauf wird durch die geregelt gc.pruneexpire, gc.reflogexpireund gc.reflogexpireunreachableEinstellungen. Vgl. git help config.

Die Standardeinstellungen sind alle recht vernünftig.

Aristoteles Pagaltzis
quelle
2
Sie sagen also im Grunde, dass die Rückflüsse für baumelnde Commits nach einer Weile automatisch entfernt werden?
MoralCode
2
Grundsätzlich: ja - nur dass die Frage etwas verwirrt ist. Ich sage, dass alle Reflog-Einträge nach einer Weile automatisch entfernt werden, aber Sie können dies durch Konfigurationseinstellungen ändern. Und weil ein Commit nur dann als Dangling bezeichnet wird, wenn nichts darauf hinweist - einschließlich Reflog-Einträgen -, sind „Reflogs für Dangling-Commits“ keine Sache. Sie wären "Reflogs für nicht erreichbare Commits".
Aristoteles Pagaltzis
"Sie wären" Reflogs für nicht erreichbare Commits "." Sie sagten jedoch: "Commits, auf die in Ihrem Reflog Bezug genommen wird, gelten als erreichbar." Wie kann "Reflogs für nicht erreichbare Commits" eine Sache sein? Ich bin so verwirrt.
LarsH
1
Ja, ich war nicht konsequent. Normalerweise denken die Leute nicht an das Reflog, und wenn sie "nicht erreichbar" sagen, bedeutet dies "von einem Schiedsrichter". Auch git help glossarylegt fest , dass , wie es ... während seine Definition für „erreichbar“ down diese Weise nicht verengt ist, so dass sie widersprüchlich sind. Witzig - also stimmt das, was ich gesagt habe, tatsächlich mit der Verwirrung in gitglossary... Es sind jedoch nicht die Konzepte, die verwirrend sind, sondern nur die Terminologie. Der Punkt ist, dass „baumelnde“ Commits solche sind, auf die sonst nichts hinweist. Wäre es hilfreich, wenn ich "Reflogs für ansonsten nicht erreichbare Commits" sage ?
Aristoteles Pagaltzis
Das ist alles sehr verwirrend. Machen wir es einfach. Wenn masterSie git commitsich in einem Zweig befinden , erhalten Sie ein Commit 000001. Dann tust du es git commit --amend, was dir ein Commit gibt 000002. Es gibt keine Tags oder Zweige 000001mehr, die darauf verweisen , und Sie können es ohne die --reflogOption nicht in Ihrem Protokoll sehen , aber wenn Sie möchten, können Sie trotzdem mit darauf zugreifen git checkout 000001. Die Frage ist nun: Ist 000001ein baumelndes Commit oder ein nicht erreichbares Commit oder keines oder beides?
Chharvey
264

Gehen Sie folgendermaßen vor, um alle baumelnden und aus den Reflogs erreichbaren Commits zu entfernen:

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

Aber seien Sie sicher, dass dies das ist, was Sie wollen. Ich empfehle Ihnen, die Manpages zu lesen, aber hier ist das Wesentliche:

git gcEntfernt nicht erreichbare Objekte (Commits, Bäume, Blobs (Dateien)). Ein Objekt ist nicht erreichbar, wenn es nicht Teil des Verlaufs eines Zweigs ist. Eigentlich ist es etwas komplizierter:

git gc macht einige andere Dinge, aber sie sind hier nicht relevant und nicht gefährlich.

Nicht erreichbare Objekte, die jünger als zwei Wochen sind, werden nicht entfernt. Daher verwenden wir --prune=now"nicht erreichbare Objekte entfernen, die zuvor erstellt wurden".

Objekte können auch über das Reflog erreicht werden. Während Zweige die Geschichte eines Projekts aufzeichnen, zeichnen Reflogs die Geschichte dieser Zweige auf. Wenn Sie Änderungen vornehmen, zurücksetzen usw., werden Commits aus dem Zweigverlauf entfernt, aber git behält sie bei, falls Sie feststellen, dass Sie einen Fehler gemacht haben. Reflogs sind eine bequeme Methode, um herauszufinden, welche destruktiven (und anderen) Operationen an einem Zweig (oder HEAD) ausgeführt wurden, und erleichtern so das Rückgängigmachen einer destruktiven Operation.

Wir müssen also auch die Reflogs entfernen, um tatsächlich alles zu entfernen, was von einem Zweig nicht erreichbar ist. Wir tun dies, indem wir --allReflogs ablaufen lassen. Wieder behält git ein bisschen von den Reflogs bei, um Benutzer zu schützen, also müssen wir ihm erneut sagen, dass er dies nicht tun soll : --expire-unreachable=now.

Da ich das Reflog hauptsächlich verwende, um mich von destruktiven Operationen zu erholen, verwende ich normalerweise --expire=nowstattdessen, wodurch die Reflogs vollständig gezappt werden.

Tarsius
quelle
1
Ich sage Ihnen, welche Befehle zu verwenden sind, was nicht offensichtlich ist - sollte gc nicht genug sein? Wenn Sie noch nie Git-Reflog verwendet haben, werden Sie es nicht wissen. Nachdem Sie nun wissen, welche Befehle Sie verwenden müssen, sollten Sie die genannten Optionen in den entsprechenden Manpages nachschlagen. Natürlich könnte ich stattdessen einfach diese Informationen von dort kopieren ...
Tarsius
1
Naja eigentlich sage ich genau was es macht: "entferne alle baumelnden Commits und die erreichbaren aus den Reflogs". Wenn Sie nicht wissen, was Reflogs sind: Lesen Sie erneut das Handbuch.
Tarsius
7
Während die gegebene Antwort richtig sein mag, weist @ erikb85 richtig darauf hin, dass es keine Aufklärung darüber gab, was Ihnen gesagt wurde. Das Follow-up mit RTFM ist noch weniger hilfreich. Ja, wir sollten alle die gesamte Dokumentation lesen. In einigen Fällen durchsucht die Person, die die Suche durchführt, die Dokumentation möglicherweise nicht ausreichend, um zu wissen, was vor sich geht. Ein bisschen Aufklärung darüber, was die Befehle tun, wäre also hilfreich für alle, die diese Antwort später finden.
Lee Saferite
@ LeeSaferite hoffe, Sie sind jetzt alle glücklich :-)
Tarsius
12
git reflog expire --expire-unreachable=now --alllässt alle deine Verstecke fallen!
Vsevolod Golovanov
22

Ich hatte das gleiche Problem, nachdem ich alle Ratschläge in diesem Thread befolgt hatte:

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

Wenn es kein Reflog und kein Zweig ist, ... muss es ein Tag sein !

git tag                             # showed several old tags created before the cleanup

Ich entfernte die Tags mit git tag -d <tagname>und überarbeitete die Bereinigung, und die alten Commits waren weg.

jakub.g
quelle
Es gibt bereits eine Antwort zu Tags ( stackoverflow.com/a/37335660/450127 ), und es scheint nicht, dass dies etwas Neues hinzufügt. Sollte dies nicht zugunsten der früheren Antwort entfernt werden?
Ian Dunn
In der Tat habe ich diese Antwort irgendwie übersehen. Obwohl 4 Leute meine Antwort hilfreich fanden, ist sie vielleicht nicht so nutzlos? Außerdem habe ich alle Möglichkeiten in einer kurzen Antwort zusammengefasst.
Jakub.g
1
Selbst wenn diese Seite dupliziert wird, wird sie möglicherweise in Google Result angezeigt und hilft Personen mit demselben Problem sofort, besser als nur Personen immer wieder auf Links umzuleiten, die möglicherweise die richtige Antwort haben.
Alexandre T.
14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

muss wohl nur sein

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

auch über Filialen von Fernbedienungen zu berichten

sehe sehen
quelle
danke, jetzt habe ich meine Fernbedienungen / origin / next gefunden, die noch dieses Commit enthalten. wie man es entfernt? git push -d origin nexthilft nicht.
iRaS
danke - das git fetch --prunehat den Trick gemacht. In allen Antworten fehlt jedoch eine Überprüfung auf Tags, die auf dieses Commit verweisen. Ich weiß immer noch nicht, wie ich mit einem Commit nach Tags suchen soll (ich habe alle entfernt).
iRaS
Aber ... bedeutet dies, dass Commits, die nur von entfernten Zweigen (und keinen lokalen Zweigen) erreichbar sind, als erreichbar angesehen werden und daher git fsck --unreachabletatsächlich über das Netzwerk mit dem entfernten kommunizieren, um herauszufinden, welche Commits erreichbar sind?
LarsH
1
Beantwortete meine eigene Frage ... Ja, Commits, die nur von entfernten Filialen (und keinen lokalen Filialen) erreichbar sind, gelten als erreichbar. Sie müssen jedoch git fsck --unreachablenicht über das Netzwerk mit der Remote kommunizieren, um herauszufinden, welche Remote-Zweige welche Commits enthalten. Die Remote-Zweigstelleninformationen werden lokal gespeichert, z. B. unter .git/refs/remotes/origin(oder in packed-refs).
LarsH
8

Ich hatte ein ähnliches Problem. Ich lief git branch --contains <commit>, und es gab keine Ausgabe wie in der Frage zurück.

Aber auch nach dem Laufen

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

Mein Commit war immer noch zugänglich mit git show <commit>. Dies lag daran, dass eines der Commits in seinem abgetrennten / baumelnden "Zweig" markiert war. Ich entfernte das Tag, führte die obigen Befehle erneut aus und war golden. git show <commit>zurückgegeben fatal: bad object <commit>- genau das, was ich brauchte. Hoffentlich hilft das jemand anderem, der genauso feststeckte wie ich.

Andrew Larsson
quelle
Wie haben Sie das Tag entfernt?
Bapors
@bapors Listet alle Tags auf, sucht das, das auf das betreffende Commit verweist, und löscht es dann. stackoverflow.com/questions/5480258/…
Andrew Larsson
4

Ich bin versehentlich auf die gleiche Situation gestoßen und habe festgestellt, dass meine Stashes Verweise auf das nicht erreichbare Commit enthalten. Daher war das vermutete nicht erreichbare Commit über Stashes erreichbar.

Dies war, was ich getan habe, um es wirklich unerreichbar zu machen.

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now
Lei Zhao
quelle
2

git gc --prune=<date>Standardmäßig werden Objekte beschnitten, die älter als zwei Wochen sind. Sie können ein neueres Datum festlegen. Bei git-Befehlen, die lose Objekte erstellen, wird jedoch im Allgemeinen git gc --auto ausgeführt (wodurch lose Objekte beschnitten werden, wenn ihre Anzahl den Wert der Konfigurationsvariablen gc.auto überschreitet).

Sind Sie sicher, dass Sie diese Commits löschen möchten? Die Standardeinstellung von gc.auto stellt sicher, dass die losen Objekte nicht unangemessen viel Speicherplatz beanspruchen. Das Speichern loser Objekte für einige Zeit ist im Allgemeinen eine gute Idee. Auf diese Weise können Sie es wiederherstellen, wenn Sie morgen feststellen, dass Ihr gelöschter Zweig ein von Ihnen benötigtes Commit enthält.

dublev
quelle