Warum scheitert `git stash -p` manchmal?

73

Ich ♥ git stash -p. Aber manchmal nach einer Befriedigung Sitzung y, nund sbekomme ich diese:

Saved working directory and index state WIP on foo: 9794c1a lorum ipsum
error: patch failed: spec/models/thing_spec.rb:65
error: spec/models/thing_spec.rb: patch does not apply
Cannot remove worktree changes

Warum?

John Bachir
quelle
Sieht nach einem ziemlich eindeutigen Fehler aus ... er ist fehlgeschlagen, als ich das erste Mal versucht habe, ihn zu teilen (und nur einen der beiden resultierenden Hunks zu behalten). Was den Grund betrifft ... offensichtlich generiert es zu einem bestimmten Zeitpunkt in seinem Betrieb nicht den richtigen Patch (wahrscheinlich beim Zurücksetzen der ausgewählten Änderungen), aber für die Details gehe ich möglicherweise zur Mailingliste, es sei denn, Sie haben Zeit zum Tauchen in die Quelle.
Cascabel
1
Ich grabe tiefer; Ich werde irgendwann etwas an die Mailingliste senden und hier posten, wenn ich es tue. Ich möchte sehen, ob ich tatsächlich herausfinden kann, wie ich das Problem beheben kann, anstatt ihnen nur zu sagen, dass dies git add--interactive --patch=stashnicht richtig funktioniert.
Cascabel
3
Puh, zu viel mit Indizes für einen Freitagabend. Meine E-Mail an die Liste ist hier .
Cascabel
13
In der Zwischenzeit können Sie dies umgehen, indem add -pSie einfach alles, was Sie behalten möchten, in den Index aufnehmen und dann stash --keep-indexdie anderen Teile verstauen.
Cascabel
Hier ist das gleiche Problem mit git add -p: gist.github.com/nh2/…
nh2

Antworten:

8

git stash -psollte mit Git 2.17 (Q2 2018) weniger scheitern.
Zuvor war " git add -p" (mit dem die Logik geteilt wird git stash) beim Zusammenführen von geteilten Patches faul, bevor das Ergebnis an das zugrunde liegende " git apply" übergeben wurde, was zu Eckfallfehlern führte. Die Logik zum Vorbereiten des Patches, der angewendet werden soll, nachdem die Hunk-Auswahl verschärft wurde.

Siehe Commit 3a8522f , Commit b3e0fcf , Commit 2b8ea7f (05. März 2018), Commit fecc6f3 , Commit 23fea4c , Commit 902f414 (01. März 2018) und Commit 11489a6 , Commit e4d671c , Commit 492e60c (19. Februar 2018) von Phillip Wood ( phillipwood) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 436d18f , 14. März 2018)

add -p: Passen Sie die Offsets nachfolgender Hunks an, wenn einer übersprungen wird

(Hinzufügen, kann aber auch auf Stash angewendet werden)

Da Commit 8cbd431 (" git-add--interactive: Hunk Recounting durch Apply --recount ersetzen", 2008-7-2, Git v1.6.0-rc0), wenn ein Hunk übersprungen wird, verlassen wir uns auf die Kontextzeilen, um nachfolgende Hunks an der richtigen Stelle anzuwenden.

Während dies die meiste Zeit funktioniert, ist es möglich, dass Hunks am falschen Ort angewendet werden.

Um dies zu beheben, passen Sie den Versatz nachfolgender Hunks an, um Änderungen in der Anzahl der Einfügungen oder Löschungen aufgrund des übersprungenen Hunks zu korrigieren. Die Änderung des Offsets aufgrund bearbeiteter Hunks, bei denen die Anzahl der Einfügungen oder Löschungen geändert wurde, wird hier ignoriert und beim nächsten Festschreiben behoben.

Hier können Sie einige Tests sehen .


Git 2.19 verbessert sich git add -p: Wenn der Benutzer den Patch in " git add -p" bearbeitet und der Editor des Benutzers so eingestellt ist, dass nachgestellte Leerzeichen wahllos entfernt werden, wird eine leere Zeile, die im Patch unverändert bleibt, vollständig leer (anstelle einer Zeile mit einem einzigen SP darauf).
Der in Git 2.17 eingeführte Code konnte einen solchen Patch nicht analysieren, aber jetzt lernte er, die Situation zu bemerken und damit umzugehen.

Siehe Commit f4d35a6 (11. Juni 2018) von Phillip Wood ( phillipwood) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 5eb8da8 , 28. Juni 2018)

add -p: Das Zählen leerer Kontextzeilen in bearbeiteten Patches wurde korrigiert

recount_edited_hunk()In Commit 2b8ea7f ("add -p: Offset-Delta für bearbeitete Patches berechnen", 2018-03-05, Git v2.17.0) eingeführt, mussten alle Kontextzeilen mit einem Leerzeichen beginnen, leere Zeilen werden nicht gezählt.
Dies sollte Probleme beim Nacherzählen vermeiden, wenn der Benutzer am Ende beim Bearbeiten des Patches leere Zeilen eingefügt hatte.

Dies führte jedoch zu einer Regression in ' git add -p', da es anscheinend üblich ist, dass Redakteure beim Bearbeiten von Patches das nachfolgende Leerzeichen von leeren Kontextzeilen entfernen, wodurch leere Zeilen eingeführt werden, die gezählt werden sollten.
'git apply' weiß, wie man mit solchen Leerzeilen umgeht, und POSIX gibt an, dass die Implementierung definiert ist, ob in einer leeren Kontextzeile ein Leerzeichen vorhanden ist oder nicht (siehe Befehl diff ).

Korrigieren Sie die Regression, indem Sie Zeilen zählen, die ausschließlich aus einer neuen Zeile bestehen, sowie Zeilen, die mit einem Leerzeichen als Kontextzeilen beginnen, und einen Test hinzufügen, um zukünftige Regressionen zu verhindern.


Git 2.23 (Q3 2019) verbessert das git add -pvon " git checkout -p" verwendete, das selektiv einen Patch in umgekehrter Reihenfolge anwenden muss: Es hat vorher nicht gut funktioniert.

Siehe Commit 2bd69b9 (12. Juni 2019) von Phillip Wood ( phillipwood) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 1b074e1 , 09. Juli 2019)

add -p: Fix checkout -pmit pathologischem Kontext

Commit fecc6f3 (" add -p: Offsets nachfolgender Hunks anpassen, wenn einer übersprungen wird", 2018-03-01, Git v2.17.0-rc0) hat das Hinzufügen von Hunks an der richtigen Stelle behoben, wenn ein vorheriger Hunk übersprungen wurde.

Patches, die umgekehrt angewendet werden, wurden jedoch nicht behandelt.

In diesem Fall müssen wir den Vorbildversatz so anpassen, dass beim Umkehren des Patches der Nachbildversatz korrekt angepasst wird.
Wir subtrahieren das Delta, anstatt es zu addieren, wenn das Patch umgekehrt wird (der einfachste Weg, darüber nachzudenken, besteht darin, einen Teil der übersprungenen Löschungen zu berücksichtigen - in diesem Fall möchten wir den Versatz reduzieren, sodass wir subtrahieren müssen).


Mit Git 2.25 (Q1 2020) werden die Bemühungen git-add--interactivefortgesetzt, das Perl-Skript nach C zu verschieben.

Infolgedessen werden die oben genannten Korrekturen erneut implementiert.

Sehen Sie verpflichten 2e40831 , begehen 54d9d9b , begehen ade246e , begehen d6cf873 , begehen 9254bdf , begehen bcdd297 , begehen b38dd9e , begehen 11f2c0d , begehen 510aeca , begehen 0ecd9d2 , begehen 5906d5d , begehen 47dc4fd , begehen 80399ae , begehen 7584dd3 , begehen 12c24cf , begehen 25ea47a , verpflichten e3bd11b , Commit 1942ee4 , Commit f6aa7ec (13. Dezember 2019) vonJohannes Schindelin ( dscho) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 45b96a6 , 25. Dezember 2019)

built-in add -p: Passen Sie die Hunk-Header nach Bedarf an

Unterzeichnet von: Johannes Schindelin

Wenn Sie einen Hunk überspringen, der eine andere Anzahl von Zeilen hinzufügt als entfernt, müssen Sie die nachfolgenden Hunk-Header von nicht übersprungenen Hunks anpassen: In pathologischen Fällen reicht der Kontext nicht aus, um genau zu bestimmen, wo der Patch angewendet werden soll.

Dieses Problem wurde in 23fea4c240 (" t3701: addTest für pathologische Kontextlinien fehlgeschlagen", 2018-03-01, Git v2.17.0-rc0 - Merge ) identifiziert und in der Perl-Version in fecc6f3a68 behoben (" add -p: Offsets nachfolgender Hunks anpassen, wenn man wird übersprungen ", 2018-03-01, Git v2.17.0-rc0 - merge ).

Und dieser Patch behebt es in der C-Version von git add -p.

Im Gegensatz zur Perl-Version versuchen wir, den zusätzlichen Text im Hunk-Header (der normalerweise die Signatur der Funktion enthält, deren Code im Hunk geändert wird) intakt zu halten.

Hinweis: Während die C-Version derzeit keine Änderungen des Staging-Modus unterstützt, bereiten wir uns bereits darauf vor, indem wir einfach den Hunk-Header überspringen, wenn sowohl der alte als auch der neue Offset 0 sind (dies kann bei regulären Hunks nicht passieren, und wir werden dies als verwenden Indikator dafür, dass es sich um ein besonderes Stück handelt).

Ebenso bereiten wir uns bereits auf die Hunk-Aufteilung vor, indem wir das Fehlen von zusätzlichem Text im Hunk-Header ordnungsgemäß behandeln: Nur der erste geteilte Hunk hat diesen Text, die anderen nicht (angezeigt durch einen leeren Start- / Endbereich für zusätzlichen Text). Die Vorbereitung auf die Hunk-Aufteilung bereits in diesem Stadium vermeidet eine spätere Einrückungsänderung des gesamten Hunk-Header-Druckblocks und ist fast so einfach zu überprüfen wie ohne diese Behandlung.


Vor Git 2.27 (Q2 2020) git stash -pfunktioniert es nicht gut , dem Benutzer das Teilen eines Patch-Hunks zu ermöglichen, während " " nicht gut funktioniert. Ein Pflaster wurde hinzugefügt, damit dies (teilweise) besser funktioniert.

Siehe Commit 7723436 , Commit 121c0d4 (08. April 2020) von Johannes Schindelin ( dscho) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit e81ecff , 28. April 2020)

stash -p: (teilweise) Fehler bezüglich Split Hunks behoben

Unterzeichnet von: Johannes Schindelin

Beim Versuch, einen Teil der Änderungen des Arbeitsbaums zu verstauen, indem ein Teil aufgeteilt und dann die geteilten Teile nur teilweise akzeptiert werden, wird dem Benutzer ein ziemlich kryptischer Fehler angezeigt:

error: patch failed: <file>:<line>
error: test: patch does not apply
Cannot remove worktree changes

und der Befehl würde nicht in der Lage sein, die gewünschten Teile der Änderungen am Arbeitsbaum zu speichern (selbst wenn die Referenz stashtatsächlich korrekt aktualisiert wurde).

Wir haben sogar einen Testfall, der diesen Fehler demonstriert und ihn bereits seit vier Jahren trägt.

Die Erklärung: Beim Teilen eines Hunks werden die geänderten Zeilen nicht mehr durch mehr als 3 Zeilen getrennt (dies ist die Anzahl der Kontextzeilen, die Git-Diffs standardmäßig verwenden) , sondern weniger.

Wenn Sie also nur einen Teil des Diff-Hunks zum Verstecken bereitstellen, enthält das resultierende Diff, das wir umgekehrt auf den Arbeitsbaum anwenden möchten, die Änderungen, die von drei Kontextlinien umgeben werden sollen. Da sich das Diff jedoch eher auf HEAD als auf das bezieht Arbeitsbaum, diese Kontextzeilen stimmen nicht überein.

Beispielzeit. Nehmen wir an, dass die Datei README folgende Zeilen enthält:

We
the
people

und der Arbeitsbaum fügte einige Zeilen hinzu, so dass er stattdessen diese Zeilen enthält:

We
are
the
kind
people

Wenn der Benutzer versucht, die Zeile mit "are" zu speichern, führt der Befehl diese Zeile intern in eine temporäre Indexdatei um und versucht, den Unterschied zwischen HEAD und dieser Indexdatei zurückzusetzen.
Das Diff-Stück, das git stashversucht, zurückzukehren, sieht ungefähr so ​​aus:

@@ -1776,3 +1776,4
 We
+are
 the
 people

Es ist jetzt offensichtlich, dass sich die nachfolgenden Kontextlinien mit dem Teil des ursprünglichen Diff-Hunks überlappen, den der Benutzer nicht verstauen wollte.

Unter Berücksichtigung der Tatsache, dass Kontextzeilen in Diffs dem Hauptzweck dienen, die genaue Position zu ermitteln, wenn das Diff nicht genau zutrifft (wenn sich die genaue Zeilennummer in der zu patchenden Datei von der im Diff angegebenen Zeilennummer unterscheidet), arbeiten wir Um dies zu umgehen, reduzieren Sie die Anzahl der Kontextzeilen: Der Diff wurde gerade generiert.

Hinweis: Dies ist keine vollständige Lösung für das Problem.
Wie im Testfall "add -p funktioniert mit pathologischen Kontextlinien" von t3701 gezeigt, gibt es im diff-Format Mehrdeutigkeiten. In der Praxis ist es natürlich sehr selten, auf solche wiederholten Linien zu stoßen.

Die vollständige Lösung für solche Fälle wäre, den Ansatz zu ersetzen, ein Diff aus dem Stash zu generieren und es dann umgekehrt durch Emulieren anzuwenden git revert(dh eine 3-Wege-Zusammenführung durchzuführen). Dies git stash -pwürde jedoch nicht für HEADden Arbeitsbaum gelten, sondern für diesen, was die Implementierung dieser nicht trivial macht, solange wir auch eine Skriptversion von verwalten add -i.


Git 2.29 (Q4 2020) bringt einen Leakfix zu git add -p(verwendet von stash -p)

Siehe Commit 324efcf (07. September 2020) von Phillip Wood ( phillipwood) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 3ad8d3e , 18. September 2020)

add -p: Speicherleck beheben

Unterzeichnet von: Phillip Wood
Bestätigt von: Johannes Schindelin

asan berichtet, dass die C-Version von add -pnicht den gesamten zugewiesenen Speicher freigibt .

struct Beheben Sie dies, indem Sie eine Funktion zum Löschen von add_p_state`` einführen und verwenden, anstatt einzelne Mitglieder freizugeben.

VonC
quelle
Die Erklärung des Verhaltensunterschieds zwischen den Versionen 2.17 und 2.19 ist sehr hilfreich.
Axel
28

Dies passiert für mich jedes Mal, wenn ich versuche, ein Stück in kleinere Stücke aufzuteilen, die zu nahe beieinander liegen (weniger als 3 Zeilen zwischen den Änderungen). Die kurze Erklärung ist, dass der Patch Kontextzeilen enthält, die mit Ihren lokalen Änderungen in Konflikt stehen. Vollständigere Erklärung unten.


Angenommen, ich habe ein Git-Repo mit diesen nicht festgeschriebenen Änderungen:

--- a/pangram
+++ b/pangram
@@ -1,8 +1,8 @@
 The
-quick
+relatively quick
 brown
 fox
-jumps
+walks
 over
 the
 lazy

Wenn ich die erste Änderung verwahre, bekomme ich:

--- a/pangram
+++ b/pangram
@@ -1,5 +1,5 @@
 The
-quick
+relatively quick
 brown
 fox
 jumps

Der git stashBefehl kann den Patch tatsächlich erfolgreich speichern (überprüfen git stash list), aber dann verwendet git diesen Patch in umgekehrter Reihenfolge, um die versteckten Änderungen aus meinem Arbeitsverzeichnis zu entfernen. Der Kontext nach dem Stück hat "Sprünge", was nicht mit den "Spaziergängen" übereinstimmt, die sich noch in meinem Arbeitsverzeichnis befinden. Also steigt git mit aus

Fehler: Patch fehlgeschlagen: Pangram: 1
Fehler: Pangram: Patch gilt nicht
Arbeitsbaumänderungen können nicht entfernt werden

und lässt alle Änderungen in meinem Arbeitsverzeichnis, und das Versteck wird so ziemlich wertlos.


Ich würde dies einen Fehler in der Unterstützung von Git's Hunk Splitting nennen. Wenn es weiß, dass die Änderungen zu eng aufgeteilt werden, kann es einige Kontextzeilen aus dem Patch entfernen oder den Patch so verschieben, dass die geänderten Kontextzeilen anstelle der makellosen vorhanden sind. Wenn das Teilen von Hunks, die so eng sind, offiziell nicht unterstützt wird, sollte es alternativ abgelehnt werden, Hunks, die so nahe sind, zu teilen.

Mu Mind
quelle
Ich glaube nicht, dass es ein Fehler bei der Aufteilung des Stücks ist: Ich habe dieses Problem gerade bei einem Problem bekommen, bei git add -pdem ich mich nie dafür entschieden habe, ein Stück aufzuteilen, sondern nur y/ sagte n.
nh2
2
Sicher, es ist nicht darauf beschränkt, Hunks explizit aufzuteilen. Der Fehler liegt in der Tatsache, dass git einen Patch erstellt, der willkürlich auf einem makellosen Status basiert, anstatt auf Ihrem aktuellen Arbeitsstatus. Dies schlägt fehl, wenn Hunks zu nahe an anderen Änderungen sind. Das häufigste Szenario ist das explizite Aufteilen von Hunks, aber anscheinend ist dies nicht das einzige Szenario.
Mu Mind
6

Nachdem git stash -pich auf die gleiche Weise einen Fehler gemacht hatte, hatte ich Glück mit dieser Problemumgehung (Git 2.0.2):

  • git add -pAufteilen der exakt gleichen Teile, jedoch mit umgekehrten Antworten ("y", um add"Änderungen beizubehalten", "n", um stashÄnderungen beizubehalten.)
  • git stash -k um den Index zu behalten und alles andere zu verstauen
  • git reset um weiter an meinen Dateien zu arbeiten

Ich bin mir nicht sicher, warum ich nicht git add -pauf die gleiche Weise gescheitert bin git stash -p. Ich denke, weil das Hinzufügen mit dem Index funktioniert, anstatt eine Patch-Datei zu erstellen?

Johann
quelle
Inverse Antworten führen leider nicht immer zu Unterschieden, die weit genug voneinander entfernt sind.
John Bachir
4

Die derzeit akzeptierte Antwort kann leider auch in Git 2.17 noch fehlschlagen.

Wenn Sie wie ich viel Mühe darauf verwendet haben, das perfekte Versteck aufzubauen, und diese Mühe nicht wegwerfen möchten, ist es immer noch möglich, meistens das zu bekommen, was Sie wollen:

git stash show -p | patch -p1 -R

Dies schlägt bei Ablehnungen fehl, aber die Chancen stehen gut, dass die meisten Hunks korrekt angewendet werden und Sie zumindest die Zeit sparen, alle Dateien erneut zu überprüfen.

Elliott Slaughter
quelle
Interessanter Ansatz. +1. Ich bin enttäuscht, dass 2.17 in Ihrem Fall immer noch fehlschlägt.
VonC
-2

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

Dean Jase
quelle
5
Diese Antwort ist für einen git stash popFehler geeignet , nicht für einen git stash -pFehler.
Max Nanasy