Abbrechen eines Stash-Pop in Git

257

Ich habe ein Versteck geknallt und es gab einen Zusammenführungskonflikt. Im Gegensatz zu der Frage, die als Duplikat aufgeführt ist, hatte ich bereits einige nicht festgeschriebene Änderungen im Verzeichnis, die ich behalten wollte. Ich möchte nicht nur den Zusammenführungskonflikt verschwinden lassen, sondern auch mein Verzeichnis wieder in den Zustand vor dem Pop bringen.

Ich habe es versucht git merge --abort, aber Git behauptete, es sei keine Zusammenführung im Gange. Gibt es eine einfache Möglichkeit, einen Pop abzubrechen, ohne die Änderungen zu zerstören, die ich ursprünglich im Verzeichnis hatte?

Casebash
quelle
Was Ihre nicht festgeschriebenen Änderungen betrifft: Waren diese Änderungen bereits im Index enthalten?
Jørgensen
Können Sie die Version von Git posten, die Sie verwenden?
Tinman
2
Die akzeptierte Antwort sieht kompliziert aus. Ich denke, es ist eine ziemlich gute allgemeine Praxis, niemals so etwas wie einen Versuch git stash popmit einem unreinen Arbeitsverzeichnis zu machen. In diesem Fall können Sie einfach git reset --hardund Ihr Vorrat ist noch intakt. (Dies ist mehr oder weniger das, was @ BradKochs verlinktes Thema vorschlägt)
Steven Lu
1
@StevenLu, ich stimme zu, aber das Problem kann in einem sauberen Arbeitsverzeichnis auftreten, wenn Sie die Änderungen speichern, um sie in einen anderen Zweig zu verschieben. Der Stash steht im Konflikt mit Commits, die auf dem neuen Zweig vorhanden sind und auf dem alten nicht vorhanden sind.
Jake Stevens-Haas

Antworten:

55

Ok, ich glaube ich habe "git stash unapply" ausgearbeitet. Es ist komplexer als git apply --reverseweil Sie eine umgekehrte Zusammenführungsaktion benötigen, falls eine Zusammenführung durch das durchgeführt wurde git stash apply.

Für die umgekehrte Zusammenführung müssen alle aktuellen Änderungen in den Index übernommen werden:

  • git add -u

Dann invertieren Sie das merge-recursive, was gemacht wurde von git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Jetzt bleiben Ihnen nur noch die nicht versteckten Änderungen. Sie werden im Index sein. Sie können git resetIhre Änderungen aufheben, wenn Sie möchten.

Angesichts der Tatsache, dass Ihr Original git stash applyfehlgeschlagen ist , gehe ich davon aus, dass auch das Gegenteil fehlschlagen könnte, da einige der Dinge, die rückgängig gemacht werden sollen, nicht erledigt wurden.

Hier ist ein Beispiel, das zeigt, wie die Arbeitskopie (via git status) wieder sauber wird:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)
Ben Jackson
quelle
2
Gehen danach die zuvor gespeicherten Änderungen für immer verloren oder sind sie wieder im Stash?
Brian H.
7
git stash applyLässt niemals ein Versteck fallen, und wenn eine Zusammenführung fehlschlägt, git stash popbehält es auch das Versteck bei
Xerus
Schlimme Dinge werden passieren, wenn es während des ursprünglichen Stash Pop
3ocene
286

Mein Anwendungsfall: Ich habe gerade versucht, auf den falschen Zweig zu springen, und habe Konflikte bekommen. Alles, was ich brauche, ist, den Pop rückgängig zu machen, ihn aber in der Stash-Liste zu belassen, damit ich ihn im richtigen Zweig herausspringen kann. Ich war das:

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

Einfach.

jmoz
quelle
22
Der gewünschte Vorrat bleibt also in der Vorratsliste, bis Sie einen erfolgreichen Pop haben?
Ryan Clark
52
Dies ist keine Antwort auf die ursprüngliche Frage, da dadurch lokale Änderungen gelöscht werden, die nicht festgeschrieben wurden.
Dmitry
13
@ RyanClark Siehe DavidGs Antwort unten. Grundsätzlich bleibt es ja in der Versteckliste.
fkorsa
1
Ich denke, die überwiegende Mehrheit der Benutzer, die sich in dieser Situation befinden, wird diese Lösung als ideal empfinden. Ich kann mir keine Situation vorstellen, in der ich nicht festgeschriebene Änderungen in meinem Arbeitsverzeichnis vorgenommen habe, bevor ich versucht habe, darauf zuzugreifen stash pop. Das klingt nach einem Rezept für eine Katastrophe.
Shadoninja
2
Nett. Nachdem ich dies gelesen hatte, schaute ich mir git stash pop docs an und es heißt "Das Anwenden des Status kann mit Konflikten fehlschlagen; in diesem Fall wird es nicht aus der Stash-Liste entfernt." Aus diesem Grund kann der Vorrat nach einem Zurücksetzen / Auschecken erneut geöffnet werden.
Brady Holt
48

Bearbeiten: Aus der git help stashDokumentation im Pop-Bereich:

Das Anwenden des Staates kann mit Konflikten fehlschlagen. In diesem Fall wird es nicht aus der Stash-Liste entfernt. Sie müssen die Konflikte von Hand lösen und anschließend git stash drop manuell aufrufen.

Wenn die Option --index verwendet wird, wird versucht, nicht nur die Änderungen des Arbeitsbaums, sondern auch die des Index wiederherzustellen. Dies kann jedoch fehlschlagen, wenn Sie Konflikte haben (die im Index gespeichert sind und Sie die Änderungen daher nicht mehr wie ursprünglich anwenden können).

Versuchen Sie, Ihr gesamtes Repo in ein neues Verzeichnis zu kopieren (damit Sie eine Kopie davon haben), und führen Sie Folgendes aus:

git stash show und speichern Sie diese Ausgabe irgendwo, wenn Sie sich dafür interessieren.

dann: git stash dropum den widersprüchlichen Vorrat fallen zu lassen dann:git reset HEAD

Das sollte Ihr Repo in dem Zustand belassen, in dem es vorher war (hoffentlich konnte ich Ihr Problem immer noch nicht wiederholen)

===

Ich versuche, Ihr Problem zu tadeln, aber alles, was ich bekomme, wenn ich es benutze, git stash popist:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

In einem sauberen Verzeichnis:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

Ich sehe nicht, dass Git versucht, meine Änderungen zusammenzuführen, es schlägt einfach fehl. Haben Sie irgendwelche Repro-Schritte, die wir befolgen können, um Ihnen zu helfen?

DavidG
quelle
Versuchen Sie, Änderungen in einer anderen Datei zu speichern.
Asmeurer
Der Versuch, in einer anderen Datei zu speichern, hat nicht funktioniert (siehe neue Repro-Schritte). Ich kann das Problem einfach nicht
tadeln
1
Diese Lösung funktioniert nicht, wenn im Arbeitsverzeichnis nicht festgeschriebene Änderungen vorhanden sind.
hier
@here Dies ist keine echte Lösung. Dies soll nur zeigen, dass ich das Problem des OP nicht reproduzieren kann und dass er keine Schritte angegeben hat, um dorthin zu gelangen, wo er sich gerade befindet.
DavidG
1
Das tatsächliche Szenario ist: 1) Ändern von Datei A. 2) Verstecken von Änderungen 3) Vornehmen von widersprüchlichen Änderungen in Datei A und Festschreiben (z. B. Ändern derselben Zeile) 4) Ändern von Datei B 5) Führen Sie 'git stash pop' aus. Jetzt haben Sie Konflikte und lokale Änderungen. Im Allgemeinen befinden sie sich in verschiedenen Dateien, aber Sie werden nie wissen, welche nicht widersprüchlichen geänderten Dateien aus dem Stash stammen und welche lokalen nicht bereitgestellten Änderungen sind.
Dmitry
16

Ich habe immer benutzt

git reset --merge

Ich kann mich nicht erinnern, dass es jemals gescheitert ist.

Kenn Sebesta
quelle
@GauravPaliwal, ich werde das nächste Mal einen Blick darauf werfen git reset. Wissen Sie, ob es funktional identisch ist mit git reset --merge?
Kenn Sebesta
5

Wenn Sie sich keine Gedanken über andere Änderungen machen müssen und nur zum letzten Commit zurückkehren möchten, können Sie Folgendes tun:

git reset .
git checkout .
git clean -f
hugo der hungrige
quelle
4

OK, ich glaube, ich habe es geschafft, einen Workflow zu finden, der Sie dorthin zurückbringt, wo Sie sein müssen (als hätten Sie den Pop nicht gemacht).

Nehmen Sie ein Backup vor der Hand! Ich weiß nicht, ob dies für Sie funktioniert. Kopieren Sie also Ihr gesamtes Repo, falls es nicht funktioniert.

1) Beheben Sie die Zusammenführungsprobleme und beheben Sie alle Konflikte, indem Sie alle Änderungen auswählen, die vom Patch stammen (in tortoisemerge wird dies als one.REMOETE (ihre) angezeigt).

git mergetool

2) Übernehmen Sie diese Änderungen (sie werden bereits über den Befehl mergetool hinzugefügt). Geben Sie ihm eine Commit-Nachricht von "Zusammenführen" oder etwas, an das Sie sich erinnern.

git commit -m "merge"

3) Jetzt haben Sie immer noch Ihre lokalen nicht bereitgestellten Änderungen, die Sie ursprünglich gestartet haben, mit einem neuen Commit aus dem Patch (wir können dies später entfernen). Übernehmen Sie nun Ihre nicht bereitgestellten Änderungen

git add .
git add -u .
git commit -m "local changes"

4) Kehren Sie den Patch um. Dies kann mit dem folgenden Befehl erfolgen:

git stash show -p | git apply -R

5) Übernehmen Sie diese Änderungen:

git commit -a -m "reversed patch"

6) Entfernen Sie die Patch- / Unpatch-Commits

git rebase -i HEAD^^^

Entfernen Sie daraus die beiden Zeilen mit "Zusammenführen" und "Umgekehrter Patch".

7) Holen Sie sich Ihre unveränderten Änderungen zurück und machen Sie das Commit "Lokale Änderungen" rückgängig

git reset HEAD^

Ich habe es mit einem einfachen Beispiel durchgearbeitet und es bringt Sie zurück zu dem Ort, an dem Sie sein möchten - direkt bevor der Stash gelöscht wurde, mit Ihren lokalen Änderungen und mit dem Stash, der noch zum Pop verfügbar ist.

Agentgonzo
quelle
Wenn das nicht ganz funktioniert, werden Sie hoffentlich den größten Teil des Weges dorthin zurücklegen! :-)
Agentgonzo
git stash show -p | git apply -RDas funktioniert nicht, wenn das git stash applywirklich zusammengeführt wurde. Siehe meine Antwort ...
Ben Jackson
3

Ich habe das etwas anders gelöst. Folgendes ist passiert.

Zuerst tauchte ich auf dem falschen Ast auf und bekam Konflikte. Der Stash blieb intakt, aber der Index befand sich in Konfliktlösung und blockierte viele Befehle.

Ein einfacher Abbruchgit reset HEAD der Konfliktlösung und ließ die nicht festgeschriebenen (und UNGEWÜNSCHTEN ) Änderungen.

Einige haben git co <filename>den Index auf den Ausgangszustand zurückgesetzt. Schließlich wechselte ich den Zweig mit git co <branch-name>und führte einen neuen aus git stash pop, der ohne Konflikte gelöst wurde.

pid
quelle
2

Einige Ideen:

  • Verwenden Sie git mergetooldiese Option , um die Zusammenführungsdateien in Originalteile und neue Teile aufzuteilen. Hoffentlich ist eine davon die Datei mit Ihren nicht versteckten Änderungen.

  • Wenden Sie den Diff des Stashs in umgekehrter Reihenfolge an, um nur diese Änderungen rückgängig zu machen. Sie müssen wahrscheinlich die Dateien mit den Zusammenführungskonflikten manuell aufteilen (wofür hoffentlich der obige Trick funktioniert).

Ich habe keines von beiden getestet, daher weiß ich nicht genau, ob sie funktionieren werden.

asmeurer
quelle
1

Ich könnte sauber git stash popin einem "schmutzigen" Verzeichnis reproduzieren , mit nicht festgeschriebenen Änderungen, aber noch nicht Pop, der einen Zusammenführungskonflikt erzeugt.

Wenn bei einem Zusammenführungskonflikt der von Ihnen versuchte Stash nicht verschwunden ist, können Sie versuchen, git show stash@{0}(optional mit --oursoder --theirs) zu untersuchen und mit git statisund zu vergleichen git diff HEAD. Sie sollten sehen können, welche Änderungen durch das Anwenden eines Stashs entstanden sind.

Jakub Narębski
quelle
1

Wenn DavidG richtig ist, dass es den Stash aufgrund des Zusammenführungskonflikts nicht geöffnet hat, müssen Sie lediglich Ihr Arbeitsverzeichnis bereinigen. Schnell git commitalles, was Sie interessiert. (Sie können resetoder squashdas Commit später, wenn Sie nicht fertig sind.) Dann mit allem, was Ihnen sicher ist, git resetalles andere , was git stash popin Ihr Arbeitsverzeichnis geschrieben wurde.

Robrich
quelle
1

Wenn vor dem git stash pop, wie in der Frage, keine abgestuften Änderungen vorgenommen wurden , sollten die folgenden beiden Befehle funktionieren.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

Der erste kehrt alle Zusammenführungen aus dem Vorrat um, ob erfolgreich oder nicht. Der zweite löscht alle nicht verfolgten Dateien, die durch den Stash eingeführt wurden.

Von man git stash: The working directory must match the index. Welches @DavidG darauf hinweist, schlägt stash popfehl, wenn derzeit nicht bereitgestellte geänderte Dateien in Konflikt stehen. Daher sollten wir uns keine Sorgen mehr machen müssen, um Zusammenführungskonflikte zu lösen, ohne darauf zurückzukommen HEAD. Alle verbleibenden geänderten Dateien haben dann nichts mit dem Stash zu tun und wurden vor dem geändertstash pop

Wenn es inszenierte Änderungen gab, ist mir nicht klar, ob wir uns auf dieselben Befehle verlassen können, und Sie möchten vielleicht die Technik von @Ben Jackson ausprobieren. Vorschläge geschätzt ..

Hier ist ein Testaufbau für alle verschiedenen Fälle: https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c
Hier
quelle
Ich denke, dies ist die bisher richtigste Antwort. Sehr gut durchdacht.
Agent Freitag,
0

Verwenden Sie git reflogdiese Option, um alle Änderungen in Ihrem Git-Verlauf aufzulisten. Kopieren Sie eine Aktions-ID und geben Sie eingit reset ACTION_ID

Fatih Acet
quelle
2
Git erstellt keinen Reflog-Eintrag, bevor Sie Pop (Sie müssen ein Commit haben)
Casebash
-1

Ich poste hier in der Hoffnung, dass andere meine Antwort hilfreich finden. Ich hatte ein ähnliches Problem, als ich versuchte, einen Stash-Pop auf einem anderen Zweig als dem zu machen, von dem ich ihn versteckt hatte. In meinem Fall hatte ich keine Dateien, die nicht festgeschrieben waren oder sich im Index befanden, aber dennoch in den Fall von Zusammenführungskonflikten gerieten (der gleiche Fall wie bei @pid). Wie andere bereits erwähnt haben, hat der fehlgeschlagene Git-Stash-Pop tatsächlich meinen Stash beibehalten. Dann löste ein schnelles Zurücksetzen des HEAD-Gitters sowie das Zurückkehren zu meinem ursprünglichen Zweig und das Ausführen des Stashs von dort aus mein Problem.

Victor Camacho
quelle