Abandonner une pop cachée dans Git

257

J'ai sauté une cachette et il y a eu un conflit de fusion. Contrairement à la question qui est répertoriée comme un doublon, j'avais déjà quelques modifications non validées dans le répertoire que je voulais conserver. Je ne veux pas seulement faire disparaître le conflit de fusion, mais aussi ramener mon répertoire à l'état où il était avant la pop.

J'ai essayé git merge --abort, mais git a affirmé qu'aucune fusion n'était en cours. Existe-t-il un moyen facile d'interrompre un pop sans détruire les modifications que j'avais à l'origine dans le répertoire?

Casebash
la source
Quant à vos changements non engagés: ces changements étaient-ils déjà dans l'index?
jørgensen
Pouvez-vous publier la version de git que vous utilisez.
Tinman
2
La réponse acceptée semble compliquée. Je pense que c'est une bonne pratique générale de ne jamais faire quelque chose comme une tentative git stash popsur un répertoire de travail impur. Dans ce cas, vous pouvez simplement git reset --hardet votre réserve est toujours intacte. (C'est plus ou moins ce que suggère le sujet lié de @ BradKoch)
Steven Lu
1
@StevenLu, je suis d'accord, mais le problème peut se produire dans un répertoire de travail propre si vous cachez les modifications afin de les déplacer vers une autre branche. La cachette entre en conflit avec les validations qui existent sur la nouvelle branche qui n'existaient pas sur l'ancienne.
Jake Stevens-Haas

Réponses:

55

Ok, je pense que j'ai travaillé sur "git stash inapply". C'est plus complexe que git apply --reverseparce que vous avez besoin d'une action de fusion inverse au cas où une fusion aurait été effectuée par le git stash apply.

La fusion inverse nécessite que toutes les modifications en cours soient insérées dans l'index:

  • git add -u

Inversez ensuite ce merge-recursivequi a été fait par git stash apply:

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

Maintenant, il ne vous restera plus que les modifications non cachées. Ils seront dans l'index. Vous pouvez utiliser git resetpour mettre en scène vos modifications si vous le souhaitez.

Étant donné que votre original a git stash applyéchoué, je suppose que l'inverse pourrait également échouer car certaines des choses qu'il souhaite annuler n'ont pas été effectuées.

Voici un exemple montrant comment la copie de travail (via git status) se retrouve à nouveau propre:

 $ 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
la source
2
après cela, les modifications précédemment cachées seront-elles perdues à jamais, ou sont-elles de retour dans la cachette?
Brian H.
7
git stash applyne laisse jamais de cachette, et lorsqu'une fusion échoue, elle git stash popconserve également la cachette
Xerus
De mauvaises choses se produiront s'il y avait un conflit de fusion lors de la stash pop originale
3ocene
286

Mon cas d'utilisation: j'ai juste essayé de sauter sur la mauvaise branche et j'ai eu des conflits. Tout ce dont j'ai besoin, c'est d'annuler la pop mais de la conserver dans la liste cachée pour que je puisse la sortir sur la bonne branche. J'ai fait ça:

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

Facile.

jmoz
la source
22
Donc, la cachette que vous voulez reste dans la liste de cachettes jusqu'à ce que vous ayez un pop réussi?
Ryan Clark
52
Ce n'est pas une réponse à la question d'origine car cela effacerait les changements locaux qui n'ont pas été validés.
Dmitry
13
@RyanClark Voir la réponse de DavidG ci-dessous. Fondamentalement, oui, il reste dans la liste cachée.
fkorsa
1
Je pense que la grande majorité des utilisateurs qui se trouvent dans cette situation trouveront cette solution idéale. Je ne peux pas imaginer une situation où j'ai des changements non validés dans mon répertoire de travail avant d'essayer d'y stash popentrer. Cela ressemble à une recette pour un désastre.
Shadoninja
2
Agréable. Après avoir lu ceci, j'ai regardé les documents pop de git stash et il dit "L'application de l'état peut échouer avec des conflits; dans ce cas, il n'est pas supprimé de la liste de stash.". Donc, c'est pourquoi la cachette peut être ré-éclatée après une réinitialisation / extraction.
Brady Holt le
48

Edit: De la git help stashdocumentation dans la section pop:

L'application de l'État peut échouer avec des conflits; dans ce cas, il n'est pas supprimé de la liste cachée. Vous devez résoudre les conflits à la main et appeler manuellement git stash drop par la suite.

Si l'option --index est utilisée, essaie de rétablir non seulement les modifications de l'arbre de travail, mais aussi celles de l'index. Cependant, cela peut échouer lorsque vous avez des conflits (qui sont stockés dans l'index, où vous ne pouvez donc plus appliquer les modifications telles qu'elles étaient à l'origine).

Essayez de copier en dur tous vos dépôts dans un nouveau répertoire (pour en avoir une copie) et exécutez:

git stash show et enregistrez cette sortie quelque part si vous vous en souciez.

puis: git stash droppour supprimer la cachette en conflit puis:git reset HEAD

Cela devrait laisser votre dépôt dans l'état où il était auparavant (j'espère que je n'ai toujours pas pu reprocher votre problème)

===

J'essaie de reprocher votre problème mais tout ce que j'obtiens quand usin git stash popest:

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

Dans un dir propre:

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

Je ne vois pas git essayer de fusionner mes modifications, cela échoue. Avez-vous des étapes de repro que nous pouvons suivre pour vous aider?

DavidG
la source
Essayez de cacher les modifications dans un autre fichier.
asmeurer
La tentative de stockage dans un autre fichier n'a pas fonctionné (voir les nouvelles étapes de repro). Je ne peux tout simplement pas reprocher le problème ...
DavidG
1
Cette solution ne fonctionne pas s'il y a des modifications non validées dans le répertoire de travail.
ici le
@ici Ce n'est pas une vraie solution, c'est juste pour montrer que je ne peux pas reproduire le problème que l'OP a et qu'il n'a fourni aucune étape pour arriver là où il en est.
DavidG
1
Le scénario réel est: 1) Changer le fichier A. 2) Changer les modifications 3) Apporter des modifications contradictoires dans le fichier A et valider (par exemple, changer la même ligne) 4) Changer le fichier B 5) Faire 'git stash pop'. Vous avez maintenant des conflits et des changements locaux. Généralement, ils se trouveront dans des fichiers différents, mais vous ne saurez jamais quels fichiers modifiés non conflictuels de stash et quels sont les changements locaux non organisés.
Dmitry
17

J'ai toujours utilisé

git reset --merge

Je ne me souviens pas qu'il ait jamais échoué.

Kenn Sebesta
la source
@GauravPaliwal, je vais y jeter un œil la prochaine fois que je git reset. Savez-vous s'il est fonctionnellement identique à git reset --merge?
Kenn Sebesta
5

Si vous n'avez pas à vous soucier des autres modifications que vous avez apportées et que vous souhaitez simplement revenir au dernier commit, vous pouvez faire:

git reset .
git checkout .
git clean -f
hugo der hungrige
la source
4

OK, je pense que j'ai réussi à trouver un workflow qui vous ramènera là où vous devez être (comme si vous n'aviez pas fait le pop).

FAITES UNE SAUVEGARDE AVANT LA MAIN !! Je ne sais pas si cela fonctionnera pour vous, alors copiez l'intégralité de votre repo au cas où cela ne fonctionnerait pas.

1) Corrigez les problèmes de fusion et corrigez tous les conflits en sélectionnant toutes les modifications qui proviennent du correctif (dans tortoisemerge, cela apparaît comme un. REMOETE (le leur)).

git mergetool

2) Validez ces modifications (elles seront déjà ajoutées via la commande mergetool). Donnez-lui un message de validation de «fusion» ou quelque chose dont vous vous souvenez.

git commit -m "merge"

3) Maintenant, vous aurez toujours vos modifications locales non mises en scène que vous avez commencées à l'origine, avec un nouveau commit du patch (nous pouvons nous en débarrasser plus tard). Maintenant, validez vos modifications non programmées

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

4) Inversez le patch. Cela peut être fait avec la commande suivante:

git stash show -p | git apply -R

5) Validez ces modifications:

git commit -a -m "reversed patch"

6) Débarrassez-vous des commits patch / unpatch

git rebase -i HEAD^^^

à partir de là, supprimez les deux lignes contenant «fusionner» et «patch inversé».

7) Récupérez vos modifications intangibles et annulez la validation des «modifications locales»

git reset HEAD^

Je l'ai parcouru avec un exemple simple et il vous ramène là où vous voulez être - directement avant que la cachette ne soit sautée, avec vos modifications locales et avec la cachette toujours disponible pour la pop.

agentgonzo
la source
Si cela ne fonctionne pas tout à fait, j'espère que cela vous mènera la plupart du temps là-bas! :-)
agentgonzo
git stash show -p | git apply -Rcela ne fonctionne pas si la git stash applyfusion est réelle. Voir ma réponse ...
Ben Jackson
3

J'ai résolu cela d'une manière quelque peu différente. Voici ce qui s'est passé.

Tout d'abord, j'ai sauté sur la mauvaise branche et j'ai eu des conflits. La cachette est restée intacte mais l'index était en résolution de conflit, bloquant de nombreuses commandes.

Un simple git reset HEADavorta la résolution du conflit et laissa les changements non validés (et INATTENDUS ).

Plusieurs ont git co <filename>ramené l'index à son état initial. Enfin, j'ai changé de branche avec git co <branch-name>et exécuté une nouvelle git stash pop, qui s'est résolue sans conflits.

pid
la source
2

Quelques idées:

  • Utilisez git mergetoolpour diviser les fichiers de fusion en parties originales et nouvelles. Espérons que l'un d'eux est le fichier avec vos modifications non cachées.

  • Appliquez le diff de la cachette à l'envers, pour annuler uniquement ces modifications. Vous devrez probablement diviser manuellement les fichiers avec les conflits de fusion (ce qui, espérons-le, fonctionnera ci-dessus).

Je n'ai testé ni l'un ni l'autre, donc je ne suis pas sûr qu'ils fonctionneront.

asmeurer
la source
1

Je pourrais reproduire git stash pople répertoire clean sur "dirty", avec des modifications non validées, mais pas encore pop qui génère un conflit de fusion.

Si lors d'un conflit de fusion, la cachette que vous avez essayé d'appliquer n'a pas disparu, vous pouvez essayer d'examiner git show stash@{0}(éventuellement avec --oursou --theirs) et de comparer avec git statiset git diff HEAD. Vous devriez être en mesure de voir les modifications apportées par l'application d'une stash.

Jakub Narębski
la source
1

Si DavidG a raison de ne pas avoir caché la cachette à cause du conflit de fusion, il vous suffit de nettoyer votre répertoire de travail. git commitTout ce dont vous vous souciez rapidement . (Vous pouvez le faire resetou squashle valider plus tard si vous n'avez pas terminé.) Ensuite, avec tout ce qui vous tient à cœur, git resettout le reste qui est git stash popvidé dans votre répertoire de travail.

robrich
la source
1

S'il n'y a eu aucun changement par étapes avant le git stash pop, comme dans la question, les deux commandes suivantes devraient fonctionner.

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

La première annule toutes les fusions de la cachette, réussies ou non. La seconde supprime tous les fichiers non suivis introduits par la cachette.

De man git stash: The working directory must match the index. ce que souligne @DavidG, le stash popéchouera si des fichiers modifiés actuellement non mis en conflit entrent en conflit. En tant que tel, nous ne devrions pas avoir à nous soucier de dénouer les conflits de fusion au-delà du retour HEAD. Les fichiers modifiés restants ne sont alors pas liés à la cachette et ont été modifiés avant lastash pop

S'il y a eu des changements par étapes, je ne sais pas si nous pouvons compter sur les mêmes commandes et vous voudrez peut-être essayer la technique de @Ben Jackson. Suggestions appréciées ..

Voici une configuration de test pour tous les différents cas 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
ici
la source
Je pense que c'est la réponse la plus correcte jusqu'à présent. Très bien pensé.
Agent vendredi
0

Utilisez git reflogpour répertorier toutes les modifications apportées à votre historique git. Copiez un ID d'action et tapezgit reset ACTION_ID

Fatih Acet
la source
2
Git ne crée pas d'entrée de reflog avant que vous n'apparaissiez (vous devez avoir un commit)
Casebash
-1

Je poste ici en espérant que d'autres trouveront ma réponse utile. J'ai eu un problème similaire lorsque j'ai essayé de faire un pop stash sur une branche différente de celle à partir de laquelle j'avais stashé. Dans mon cas, je n'avais pas de fichiers non validés ou dans l'index mais toujours dans le cas des conflits de fusion (même cas que @pid). Comme d'autres l'ont souligné précédemment, le pop git stash a échoué a effectivement conservé ma stash, puis un rapide git reset HEAD plus revenir à ma branche d'origine et faire la stash à partir de là a résolu mon problème.

Victor Camacho
la source