Wie kann man einen verlorenen Vorrat in Git wiederherstellen?

Ich verwende häufig git stash und git stash pop , um Änderungen in meinem Arbeitsbaum zu speichern und wiederherzustellen. Gestern hatte ich einige Änderungen in meinem Arbeitsbaum, die ich verstaut und gepoppt hatte, und dann habe ich weitere Änderungen an meinem Arbeitsbaum vorgenommen. Ich würde gerne zurückgehen und die verdeckten Änderungen von gestern überprüfen, aber git stash pop scheint alle Verweise auf das zugehörige Commit zu entfernen.

Ich weiß, dass .git / refs / stash den Verweis auf das Commit enthält , das zum Erstellen des Stash verwendet wird, wenn ich git stash verwende. Und .git / logs / refs / stash enthält den gesamten Stash. Aber diese Referenzen sind nach git stash pop gegangen. Ich weiß, dass das Commit immer noch in meinem Repository ist, aber ich weiß nicht, was es war.

Gibt es eine einfache Möglichkeit, die Stash-Commit-Referenz von gestern wiederherzustellen?

Beachten Sie, dass dies für mich heute nicht kritisch ist, da ich tägliche Backups habe und zum Arbeitsbaum von gestern zurückkehren kann, um meine Änderungen zu erhalten. Ich frage, weil es einen leichteren Weg geben muss!

   

Wenn Sie es gerade erst gepoppt haben und das Terminal noch geöffnet ist, haben Sie immer noch den Hash-Wert von git stash pop auf dem Bildschirm gedruckt (danke, Dolda).

Ansonsten können Sie es für Linux und Unix verwenden:

 git fsck --no-reflog | awk '/dangling commit/ {print $3}' 

und für Windows:

 git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];} 

Dies zeigt Ihnen alle Commits an den Spitzen Ihres Commit-Graphen, die nicht mehr von einem Zweig oder Tag referenziert werden – jede verlorene Commit, einschließlich jeder Stash Commit, die Sie jemals erstellt haben, wird irgendwo in diesem Graphen sein.

Der einfachste Weg, den gewünschten Stash-Commit zu finden, ist wahrscheinlich, diese Liste an gitk zu gitk :

 gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) 

Dadurch wird ein Repository-Browser gestartet, der jedes einzelne Commit im Repository anzeigt , unabhängig davon, ob es erreichbar ist oder nicht.

Sie können gitk dort durch etwas wie git log --graph --oneline --decorate wenn Sie ein nettes Diagramm auf der Konsole über eine separate GUI-App bevorzugen.

Um Stash-Commits zu erkennen, suchen Sie nach Commit-Nachrichten dieser Form:

WIP auf somebranch : commithash Eine alte Commit-Nachricht

Hinweis : Die Commit-Nachricht wird nur in dieser Form (beginnend mit “WIP on”) angezeigt, wenn Sie beim git stash keine Nachricht angegeben haben.

Sobald Sie den Hash der gewünschten Commit kennen, können Sie ihn als Stash verwenden:

 git stash apply $stash_hash 

Oder Sie können das Kontextmenü in gitk , um Verzweigungen für alle nicht erreichbaren Commits zu erstellen, an denen Sie interessiert sind. Danach können Sie mit allen normalen Tools machen, was Sie wollen. Wenn Sie fertig sind, blasen Sie diese Zweige einfach wieder weg.

Wenn Sie das Terminal nicht schließen, sehen Sie sich einfach die Ausgabe von git stash pop und Sie haben die Objekt-ID des gelöschten Stash. Es sieht normalerweise so aus:

 $ git stash pop [...] Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1) 

(Beachten Sie, dass git stash drop auch die gleiche Zeile erzeugt.)

Um diesen Vorrat zurück zu bekommen, git branch tmp 2cae03e einfach git branch tmp 2cae03e , und du wirst es als Zweig bekommen. Um dies in einen Stash zu konvertieren, führe folgendes aus:

 git stash apply tmp git stash 

Wenn Sie es als Zweig haben, können Sie es auch frei manipulieren. zum Beispiel, um es zu pflücken oder zu merge.

Ich wollte nur diesen Zusatz zur akzeptierten Lösung erwähnen. Es war für mich nicht sofort offensichtlich, als ich diese Methode zum ersten Mal ausprobierte (vielleicht hätte es sein sollen), aber um den Stash aus dem Hash-Wert zu übernehmen, benutze einfach “git stash apply”:

 $ git stash apply ad38abbf76e26c803b27a6079348192d32f52219 

Als ich neu bei Git war, war mir das nicht klar, und ich versuchte verschiedene Kombinationen von “Git-Show”, “Git-Anwendung”, “Patch”, etc.

Ich habe gerade einen Befehl erstellt, der mir geholfen hat, mein verlorenes Stash-Commit zu finden:

 for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less 

Dies listet alle Objekte in der .git / objects-Baumstruktur auf, sucht nach den Objekten, die vom Typ commit sind, und zeigt dann eine Zusammenfassung der einzelnen Objekte an. Von diesem Punkt an war es nur eine Frage der Durchsicht der Commits, ein passendes “WIP on work” zu finden: 6a9bb2 (“work” ist mein Zweig, 619bb2 ist ein neuer Commit).

Ich stelle fest, dass ich dieses Problem nicht hätte, wenn ich “git stash apply” anstelle von “git stash pop” verwende, und wenn ich “git stash save message ” verwende, dann könnte das Commit einfacher zu finden gewesen sein.

Update: Mit Nathans Idee wird das kürzer:

 for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less 

Um die Liste der Stashes zu erhalten, die sich noch in Ihrem Repository befinden, aber nicht mehr erreichbar sind:

 git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP 

Wenn du deinem Stash einen Titel gegeben hast, ersetze “WIP” in -grep=WIP am Ende des Befehls mit einem Teil deiner Nachricht, zB -grep=Tesselation .

Der Befehl grepping für “WIP”, da die Standard-Commit-Nachricht für einen Stash in der Form WIP on mybranch: [previous-commit-hash] Message of the previous commit.

git fsck --unreachable | grep commit git fsck --unreachable | grep commit sollte das sha1 anzeigen, obwohl die Liste, die es zurückgibt, ziemlich groß sein könnte. git show zeigt an, ob es das gewünschte Commit ist.

git cherry-pick -m 1 wird das Commit auf den aktuellen Zweig zusammenführen.

Wenn du einen verlorenen Stash wiederherstellen willst, musst du zuerst den Hash deines verlorenen Stash finden.

Wie Aristoteles Pagaltzis vorgeschlagen hat, sollte dir ein git fsck helfen.

Persönlich benutze ich meinen log-all Alias, der mir jedes Commit (wiederherstellbare Commits) anzeigt, um eine bessere Sicht auf die Situation zu haben:

 git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3) 

Sie können eine noch schnellere Suche durchführen, wenn Sie nur nach “WIP on” -Nachrichten suchen.

Sobald du dein sha1 kennst, änderst du einfach dein Stash-Reflog, um das alte Stash hinzuzufügen:

 git update-ref refs/stash ed6721d 

Sie werden wahrscheinlich eine zugehörige Nachricht bevorzugen, also eine -m

 git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d 

Und Sie werden das sogar als Alias ​​verwenden wollen:

 restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1 

Ich mochte den Ansatz von Aristoteles, aber ich benutzte GITK nicht gern … wie ich es gewohnt bin, GIT von der Kommandozeile aus zu benutzen.

Stattdessen nahm ich die dangling commits und gab den Code in eine DIFF-Datei zur Überprüfung in meinem Code-Editor aus.

 git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff 

Jetzt können Sie die resultierende diff / txt-Datei (in Ihrem Home-Ordner) in Ihren TXT-Editor laden und den tatsächlichen Code und die resultierende SHA anzeigen.

Dann benutze es einfach

 git stash apply ad38abbf76e26c803b27a6079348192d32f52219 

In OSX mit git v2.6.4 starte ich einfach git stash drop aus Versehen, dann fand ich es unter den Schritten

Wenn Sie den Namen des Stash kennen, dann verwenden Sie:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2

ansonsten finden Sie die ID aus dem Ergebnis manuell mit:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Wenn Sie die Commit-ID gefunden haben, drücken Sie einfach den Git Stash apply {commit-id}

Hoffe das hilft jemandem schnell

Windows PowerShell entspricht gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

Es gibt wahrscheinlich einen effizienteren Weg, dies in einer Pipe zu tun, aber das macht den Job.

Ich möchte zu der akzeptierten Lösung einen weiteren guten Weg hinzufügen, um alle Änderungen zu durchlaufen, wenn Sie entweder gitk nicht verfügbar haben oder kein X für die Ausgabe.

 git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits for h in `cat tmp_commits`; do git show $h | less; done 

Dann erhalten Sie alle Diffs für diese Hashes nacheinander angezeigt. Drücken Sie ‘q’, um zum nächsten Diff zu gelangen.

Warum stellen Leute diese Frage? Weil sie den Reflog noch nicht kennen oder verstehen.

Die meisten Antworten auf diese Frage geben lange Befehle mit Optionen, an die sich fast niemand mehr erinnert. So kommen Leute in diese Frage und kopieren Paste, was immer sie denken sie zu brauchen und vergessen es fast sofort danach.

Ich würde jedem mit dieser Frage empfehlen, einfach den Reflog (git reflog) zu überprüfen, nicht viel mehr. Sobald Sie diese Liste aller Commits sehen, gibt es hundert Möglichkeiten, herauszufinden, welche Commit Sie suchen, und sie auszuwählen oder eine Verzweigung daraus zu erstellen. Dabei haben Sie sich über den Reflog und nützliche Optionen für verschiedene grundlegende Git-Befehle informiert.

Die akzeptierte Antwort von Aristoteles zeigt alle erreichbaren Commits, einschließlich nicht-stash-ähnlicher Commits. Um den Lärm herauszufiltern:

 git fsck --no-reflog | \ awk '/dangling commit/ {print $3}' | \ xargs git log --no-walk --format="%H" \ --grep="WIP on" --min-parents=3 --max-parents=3 

Dies beinhaltet nur Commits, die genau 3 Eltern-Commits haben (was ein Stash haben wird) und deren Nachricht “WIP on” beinhaltet.

Denken Sie daran, dass, wenn Sie Ihren Stash mit einer Nachricht gespeichert haben (z. B. git stash save "My newly created stash" ), dies die Standardmeldung “WIP on …” überschreibt.

Sie können weitere Informationen zu jedem Commit anzeigen, z. B. die Commit-Nachricht anzeigen oder an git stash show :

 git fsck --no-reflog | \ awk '/dangling commit/ {print $3}' | \ xargs git log --no-walk --format="%H" \ --grep="WIP on" --min-parents=3 --max-parents=3 | \ xargs -n1 -I '{}' bash -c "\ git log -1 --format=medium --color=always '{}'; echo; \ git stash show --color=always '{}'; echo; echo" | \ less -R 

Ich konnte keine der Antworten auf Windows in einem einfachen Befehlsfenster erhalten (in meinem Fall Windows 7). awk , grep und Select-string wurden nicht als Befehle erkannt. Also habe ich einen anderen Ansatz versucht:

  • erster Lauf: git fsck --unreachable | findstr "commit" git fsck --unreachable | findstr "commit"
  • Kopiere die Ausgabe in den Editor
  • finde ersetzen “unerreichbar commit” mit start cmd /k git show

sieht etwa so aus:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • Speichern Sie als .bat-Datei und führen Sie sie aus
  • Das Skript öffnet eine Reihe von Befehlsfenstern, die jedes Commit anzeigen
  • Wenn du den git stash apply (your hash) gefunden hast, starte: git stash apply (your hash)

vielleicht nicht die beste Lösung, aber für mich gearbeitet

Was ich hier gesucht habe, ist, wie man den Vorrat zurückbekommt, egal was ich ausgecheckt habe. Insbesondere hatte ich etwas verstaut, dann eine ältere Version ausgecheckt und dann veröffentlicht, aber das Versteck war zu diesem früheren Zeitpunkt ein No-Op, also verschwand das Versteck; Ich konnte nicht einfach den git stash , um ihn wieder auf den Stapel zu schieben. Das hat für mich funktioniert:

 $ git checkout somethingOld $ git stash pop ... nothing added to commit but untracked files present (use "git add" to track) Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179) $ git checkout 27f6bd8ba3c $ git reset HEAD^ # Make the working tree differ from the parent. $ git stash # Put the stash back in the stack. Saved working directory and index state WIP on (no branch): c2be516 Some message. HEAD is now at c2be516 Some message. $ git checkout somethingOld # Now we are back where we were. 

Rückblickend hätte ich git stash apply nicht git stash pop . Ich machte eine bisect und hatte einen kleinen Patch, den ich bei jedem bisect anwenden wollte. Jetzt mache ich das:

 $ git reset --hard; git bisect good; git stash apply $ # Run tests $ git reset --hard; git bisect bad; git stash apply etc. 

Habe es mithilfe der folgenden Schritte wiederhergestellt:

  1. Identifizieren Sie den gelöschten Stash-Hash-Code:

    gitk –all $ (git fsck –no-reflog | awk ‘/ dangle commit / {Drucken $ 3}’)

  2. Kirsche Pick den Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Lösen Sie Konflikte, wenn Sie Folgendes verwenden:

    Git mergetool

Außerdem haben Sie möglicherweise Probleme mit der Commit-Nachricht, wenn Sie Gerrit verwenden. Bitte speichern Sie Ihre Änderungen, bevor Sie die nächsten Alternativen befolgen:

  1. Verwenden Sie hard reset für das vorherige Commit und wiederholen Sie diese Änderung erneut.
  2. Sie können die Änderung auch speichern, neu erstellen und erneut eingeben.

Sie können alle nicht erreichbaren Commits auflisten, indem Sie diesen Befehl im Terminal schreiben –

 git fsck --unreachable 

Unerreichbaren Commit-Hash prüfen –

 git show hash 

Wenden Sie sich abschließend an, wenn Sie den Gegenstand finden –

 git stash apply hash 

Ein weiterer häufiger Anwendungsfall: Sie haben versucht, auf den falschen Zweig zu springen und haben Konflikte?

Alles, was Sie wollen, ist das Rückgängigmachen des Pops, aber behalten Sie es immer noch in der Stash-Liste, damit Sie es auf dem richtigen Zweig herausholen können.

Um es zu beheben, machen Sie Folgendes:

 git reset HEAD --hard git checkout my_correct_branch git stash pop 

Erledigt!

Ich habe den Speicher in der GitUP-App versehentlich gelöscht. Drücken Sie einfach Strg + Z, um es rückgängig zu machen.

Vielleicht hilft es jemandem;)