Annullare un pop stash in Git


257

Ho spuntato una scorta e c'è stato un conflitto di unione. A differenza della domanda elencata come duplicata, ho già avuto alcune modifiche non confermate nella directory che volevo mantenere. Non voglio solo far scomparire il conflitto di unione, ma anche riportare la mia directory allo stato precedente al pop.

Ci ho provato git merge --abort, ma Git ha affermato che non era in corso alcuna fusione. C'è un modo semplice per interrompere un pop senza distruggere le modifiche che avevo originariamente nella directory?


Per quanto riguarda le modifiche non impegnate: queste modifiche erano già nell'indice?
jørgensen,

Puoi pubblicare la versione di Git che stai utilizzando.
Tinman,


2
La risposta accettata sembra complicata. Penso che sia una buona pratica generale non fare mai qualcosa come tentare git stash popsu un dir di lavoro impuro. Nel qual caso puoi semplicemente git reset --harde la tua scorta è ancora intatta. (Questo è più o meno ciò che suggerisce l'argomento collegato di @ BradKoch)
Steven Lu,

1
@StevenLu, sono d'accordo, ma il problema può verificarsi in una directory di lavoro pulita se si nascondono le modifiche per spostarle in un altro ramo. La scorta è in conflitto con i commit che esistono sul nuovo ramo che non esistevano su quello vecchio.
Jake Stevens-Haas,

Risposte:


55

Ok, penso di aver elaborato "git stash inapplicabile". È più complesso rispetto al git apply --reversefatto che è necessaria un'azione di fusione inversa nel caso in cui ci sia stata una fusione effettuata da git stash apply.

L'unione inversa richiede che tutte le modifiche correnti vengano inserite nell'indice:

  • git add -u

Quindi inverti merge-recursiveciò che è stato fatto da git stash apply:

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

Ora ti rimarranno solo le modifiche non stash. Saranno nell'indice. Se lo git resetdesideri, puoi utilizzare per annullare la messa in scena delle modifiche.

Dato che il tuo originale git stash applyfallito, suppongo che anche il contrario potrebbe fallire poiché alcune delle cose che vuole annullare non sono state fatte.

Ecco un esempio che mostra come la copia di lavoro (via git status) finisce nuovamente pulita:

 $ 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)

2
dopo aver fatto ciò, le modifiche precedentemente nascoste andranno perse per sempre o torneranno nella scorta?
Brian H.

7
git stash applynon rilascia mai uno stash e quando un'unione fallisce, git stash popconserva anche lo stash
Xerus

Succederanno cose brutte se ci fosse un conflitto di unione durante l'originale pop stash
3ocene

286

Il mio caso d'uso: ho appena provato a spuntare sul ramo sbagliato e ho avuto conflitti. Tutto ciò di cui ho bisogno è annullare il pop ma tenerlo nell'elenco degli stash in modo da poterlo visualizzare sul ramo corretto. L'ho fatto:

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

Facile.


22
Quindi lo stash che desideri rimane nell'elenco degli stash fino a quando non hai un pop di successo?
Ryan Clark,

52
Questa non è una risposta alla domanda originale in quanto cancellerebbe le modifiche locali che non sono state impegnate.
Dmitry,

13
@RyanClark Vedi sotto la risposta di DavidG . Fondamentalmente, sì, rimane nell'elenco degli stash.
fkorsa,

1
Penso che la stragrande maggioranza degli utenti che si trovano in questa situazione troveranno questa soluzione ideale. Non riesco a immaginare una situazione in cui ho apportato modifiche non confermate nella mia directory di lavoro prima di provare ad stash popaccedervi. Sembra una ricetta per il disastro.
Shadoninja,

2
Bello. Dopo aver letto questo, ho esaminato i documenti git stash pop e mi dice "L'applicazione dello stato può fallire con i conflitti; in questo caso, non viene rimosso dall'elenco degli stash". Quindi, questo è il motivo per cui lo stash può essere ripreso dopo un reset / checkout.
Brady Holt,

48

Modifica: dalla git help stashdocumentazione nella sezione pop:

L'applicazione dello stato può fallire con i conflitti; in questo caso, non viene rimosso dall'elenco di stash. È necessario risolvere manualmente i conflitti e chiamare manualmente git stash drop in seguito.

Se viene utilizzata l'opzione --index, cerca di ripristinare non solo le modifiche dell'albero di lavoro, ma anche quelle dell'indice. Tuttavia, ciò può non riuscire, quando si verificano conflitti (che sono memorizzati nell'indice, dove pertanto non è più possibile applicare le modifiche come erano in origine).

Prova a copiare tutti i repository in una nuova directory (in modo da averne una copia) ed esegui:

git stash show e salva quell'output da qualche parte se ti interessa.

poi: git stash drop per eliminare lo stash in conflitto quindi:git reset HEAD

Ciò dovrebbe lasciare il tuo repository nello stato precedente (si spera, non sono ancora riuscito a riproporre il tuo problema)

===

Sto cercando di riproporre il tuo problema ma tutto ciò che ottengo quando usin git stash popè:

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 una dir pulita:

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

Non vedo git che prova a unire le mie modifiche, fallisce e basta. Hai dei passaggi di riproduzione che possiamo seguire per aiutarti?


Prova a nascondere le modifiche a un altro file.
asmeurer il

Il tentativo di eseguire lo stash su un altro file non ha funzionato (vedere i nuovi passaggi della riproduzione). Non riesco proprio a riproporre il problema ...
DavidG,

1
Questa soluzione non funziona se nella directory di lavoro sono presenti modifiche senza commit.
qui,

@here Questa non è una vera soluzione, questo è solo per dimostrare che non riesco a riprodurre il problema che l'OP ha e che non ha fornito alcun passo per arrivare dove si trova.
DavidG,

1
Lo scenario effettivo è: 1) Modifica il file A. 2) Cambiamenti 3) Apporta modifiche in conflitto nel file A e esegui il commit (ad esempio cambia la stessa riga) 4) Cambia file B 5) Esegui 'git stash pop'. Ora hai conflitti e cambiamenti locali. Generalmente si troveranno in file diversi, ma non si saprà mai quali file modificati non in conflitto dallo stash e quali sono le modifiche locali non in scena.
Dmitry,

17

L'ho sempre usato

git reset --merge

Non ricordo che abbia mai fallito.


@GauravPaliwal, la prossima volta lo darò git reset. Sai se è funzionalmente identico a git reset --merge?
Kenn Sebesta,

5

Se non devi preoccuparti di altre modifiche apportate e desideri solo tornare all'ultimo commit, puoi fare:

git reset .
git checkout .
git clean -f

4

OK, penso di essere riuscito a trovare un flusso di lavoro che ti riporterà dove devi essere (come se non avessi fatto il pop).

PRENDI UN BACKUP PRIMA DI !! Non so se questo funzionerà per te, quindi copia l'intero repository nel caso in cui non funzioni.

1) Risolvi i problemi di unione e correggi tutti i conflitti selezionando tutte le modifiche che provengono dalla patch (in tortoisemerge, questo appare come one.REMOETE (loro)).

git mergetool

2) Conferma queste modifiche (saranno già aggiunte tramite il comando mergetool). Dagli un messaggio di "unione" o qualcosa che ricordi.

git commit -m "merge"

3) Ora avrai ancora le modifiche locali non messe in scena che hai iniziato originariamente, con un nuovo commit dalla patch (possiamo liberarcene in seguito). Ora esegui il commit delle modifiche non messe in scena

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

4) Invertire la patch. Questo può essere fatto con il seguente comando:

git stash show -p | git apply -R

5) Commetti questi cambiamenti:

git commit -a -m "reversed patch"

6) Sbarazzarsi dei commit di patch / unpatch

git rebase -i HEAD^^^

da questo, rimuovere le due righe con 'unisci' e 'patch inversa' al suo interno.

7) Ripristina le modifiche non programmate e annulla il commit "modifiche locali"

git reset HEAD^

L'ho passato con un semplice esempio e ti riporta dove vuoi essere - direttamente prima che la scorta fosse spuntata, con le tue modifiche locali e con la scorta ancora disponibile per essere pop.


Se non funziona, spero che ti porti la maggior parte del percorso! :-)
agentgonzo,

git stash show -p | git apply -Rche non funziona se hanno git stash applyfatto una vera fusione. Vedi la mia risposta ...
Ben Jackson,

3

L'ho risolto in un modo un po 'diverso. Ecco cosa è successo.

Innanzitutto, sono saltato sul ramo sbagliato e ho avuto conflitti. La scorta è rimasta intatta ma l'indice era in risoluzione dei conflitti, bloccando molti comandi.

Un semplice git reset HEADinterrotto la risoluzione del conflitto e lasciato le modifiche non impegnate (e indesiderate ).

Diversi hanno git co <filename>riportato l'indice allo stato iniziale. Alla fine, ho cambiato filiale git co <branch-name>ed eseguito un nuovo git stash pop, che si è risolto senza conflitti.


2

Qualche idea:

  • Utilizzare git mergetoolper dividere i file di unione in parti originali e nuove. Speriamo che uno di questi sia il file con le modifiche non stash in esso.

  • Applica il diff dello stash al contrario, per annullare solo quei cambiamenti. Probabilmente dovrai dividere manualmente i file con i conflitti di unione (per cui spero che il trucco sopra funzionerà per).

Non ho provato nessuno di questi, quindi non so per certo che funzioneranno.


1

Potrei riprodurre pulito git stash popsu directory "sporche", con modifiche non impegnate, ma non ancora pop che genera un conflitto di unione.

Se in conflitto di unione la scorta che hai provato ad applicare non è scomparsa, puoi provare a esaminare git show stash@{0}(facoltativamente con --ourso --theirs) e confrontare con git statise git diff HEAD. Dovresti essere in grado di vedere quali modifiche derivano dall'applicazione di una scorta.


1

Se DavidG ha ragione nel dire che non ha visualizzato la scorta a causa del conflitto di unione, allora devi semplicemente ripulire la directory di lavoro. Rapidamente git committutto ciò che ti interessa. (Puoi reseto squashesegui il commit più tardi se non hai finito.) Quindi, con tutto ciò che ti interessa al sicuro, git resettutto ciò che è stato git stash popscaricato nella tua directory di lavoro.


1

Se non sono state apportate modifiche graduali prima di git stash pop, come nella domanda, i seguenti due comandi dovrebbero funzionare.

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

Il primo inverte qualsiasi fusione dalla scorta, riuscita o no. Il secondo elimina tutti i file non tracciati introdotti dallo stash.

Da man git stash: The working directory must match the index. quale @DavidG sottolinea, stash popfallirà in caso di conflitto tra file modificati non messi in scena. Pertanto, non dovremmo preoccuparci di sciogliere i conflitti di unione oltre a tornare a HEAD. Tutti i file modificati rimanenti non sono quindi correlati allo stash e sono stati modificati prima distash pop

Se ci sono stati cambiamenti in scena, non sono chiaro se possiamo fare affidamento sugli stessi comandi e potresti voler provare la tecnica di @Ben Jackson. Suggerimenti apprezzati ..

Ecco una configurazione di test per tutti i vari casi 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

Penso che questa sia la risposta più corretta finora. Molto ben pensato.
Agente venerdì

0

Utilizzare git reflogper elencare tutte le modifiche apportate nella cronologia di Git. Copia un ID azione e digitagit reset ACTION_ID


2
Git non crea una voce di reflog prima del pop (è necessario disporre di un commit)
Casebash

-1

Sto postando qui sperando che altri possano trovare utile la mia risposta. Ho avuto un problema simile quando ho provato a fare un pop stash su un ramo diverso da quello da cui mi ero nascosto. Nel mio caso non avevo file che non fossero stati ripristinati o nell'indice ma rimanevo nel caso dei conflitti di unione (lo stesso caso di @pid). Come altri hanno sottolineato in precedenza, il pop stash git fallito ha davvero mantenuto la mia scorta, quindi un rapido git ha ripristinato HEAD più tornare al mio ramo originale e fare la scorta da lì ha risolto il mio problema.

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.