git stash gaffe: git stash pop et s'est retrouvé avec des conflits de fusion

200

J'ai fait un git stash popet je me suis retrouvé avec des conflits de fusion. J'ai supprimé les fichiers du système de fichiers et j'ai fait git checkoutcomme indiqué ci-dessous, mais il pense que les fichiers ne sont toujours pas fusionnés. J'ai ensuite essayé de remplacer les fichiers et de git checkoutrefaire le même résultat. J'ai essayé de le forcer avec le -fdrapeau. Toute aide serait appréciée!

chirag-patels-macbook-pro:haloror patelc75$ git status
app/views/layouts/_choose_patient.html.erb: needs merge
app/views/layouts/_links.html.erb: needs merge
# On branch prod-temp
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   db/schema.rb
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       unmerged:   app/views/layouts/_choose_patient.html.erb
#       unmerged:   app/views/layouts/_links.html.erb

chirag-patels-macbook-pro:haloror patelc75$ git checkout app/views/layouts/_choose_patient.html.erb
error: path 'app/views/layouts/_choose_patient.html.erb' is unmerged
chirag-patels-macbook-pro:haloror patelc75$ git checkout -f app/views/layouts/_choose_patient.html.erb
warning: path 'app/views/layouts/_choose_patient.html.erb' is unmerged
Chirag Patel
la source
Remarque: restaurer l'état avant le git stash apply/popdevrait être plus facile avec Git 2.5 (Q2 2015), car l'arborescence de travail doit maintenant être propre: voir ma réponse ci
VonC

Réponses:

219

Voir man git merge ( COMMENT RÉSOUDRE LES CONFLITS ):

Après avoir vu un conflit, vous pouvez faire deux choses:

  • Décidez de ne pas fusionner. Les seuls nettoyages dont vous avez besoin sont de réinitialiser le fichier d'index à la validation HEAD pour inverser 2. et de nettoyer les modifications de l'arborescence de travail effectuées par 2. et 3 .; git-reset --hard peut être utilisé pour cela.

  • Résolvez les conflits. Git marquera les conflits dans l'arborescence de travail. Modifiez les fichiers en forme et ajoutez-les à l'index. Utilisez git commit pour sceller l'accord.

Et sous TRUE MERGE (pour voir à quoi se réfèrent 2. et 3.):

Lorsqu'il n'est pas évident de réconcilier les modifications, les événements suivants se produisent:

  1. Le pointeur HEAD reste le même.

  2. La référence MERGE_HEAD est définie pour pointer vers l'autre tête de branche.

  3. Les chemins qui ont fusionné proprement sont mis à jour à la fois dans le fichier d'index et dans votre arborescence de travail.

  4. ...

Donc: utilisez git reset --hardsi vous souhaitez supprimer les modifications de cachette de votre arborescence de travail, ou git resetsi vous voulez simplement nettoyer l'index et laisser les conflits dans votre arborescence de travail fusionner à la main.

Sous man git stash ( OPTIONS, pop ) vous pouvez lire en plus:

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.

tanascius
la source
9
En fait, même après avoir déposé une stash, il est toujours possible (bien que plus difficile) de la récupérer à nouveau, car l'ensemble de modifications existe toujours dans le référentiel. stackoverflow.com/search?q=git+recover+dropped+stash
phils
3
@nalply: est-ce bon ou mauvais? Vous êtes invités à améliorer ma réponse, où vous ne l'avez pas compris en premier lieu ...
tanascius
1
Je pense que la révision du code source est un domaine complexe. Il est facile de se confondre. Je pense toujours que votre réponse est bonne car elle a confirmé mon approche.
2011
1
Non seulement cela m'a beaucoup aidé à réaliser que la cachette n'a pas été retirée comme je l'avais supposé, mais cela explique pourquoi ma cachette a continué de croître même lorsque j'étais certain que je n'avais pas oublié de récupérer des choses.
Thor84no
11
"L'application de l'état peut échouer avec des conflits; dans ce cas, il n'est pas supprimé de la liste cachée." C'est la partie la plus importante du poste, à mon avis. Pensez à modifier votre réponse pour la mettre en avant avec les mots NE PAS PANIQUE en grosses lettres amicales. (+1 déjà cependant.) Merci.
Patrick M
42

Une chose similaire m'est arrivée. Je ne voulais pas mettre en scène les fichiers pour l'instant, alors je les ai ajoutés avec git add, puis je l'ai fait git reset. Cela a simplement ajouté puis supprimé mes modifications, mais a effacé les chemins non fusionnés.

Aaron
la source
4
Cela semble être mieux que d'utiliser reset --hardcar il n'écrase pas vos fichiers (sauf ceux avec des problèmes de fusion). Merci!
sinelaw
Je ne voulais pas encore mettre en scène les fichiers, alors je les ai ajoutés - ne met pas en addscène le contenu de l'arborescence de travail vers l'index? Je ne pense pas comprendre pourquoi votre réponse fonctionne à partir de la description.
Drew Noakes
2
git addles met en scène mais git reset, ce que je fais immédiatement après, les met en scène. Essentiellement, il efface les chemins non fusionnés et me ramène à mon arbre de travail normal en simulant git out.
Aaron
3
Vous n'en avez pas besoin git addsi vous le souhaitez git reset. Le git reset"défait" effectivement le git add. git reset( --mixed<- par défaut) ne touche effectivement pas le répertoire de travail, donc exactement ce qui était dans votre répertoire de travail, les conflits de fusion et tout, sont laissés seuls. L'index (et techniquement la tête de branche) est cependant réinitialisé (sans référence, ils sont réinitialisés HEAD, ce qui signifie probablement aucun changement pour la tête de branche, et annule efficacement tout ce qui a été git addfait pour l'index, ainsi que l'effacement de l'état des chemins non fusionnés) .
bambams
3
La séquence , modifier / résolution , git resetet git stash dropfonctionne bien. Il fait ce git stash popqu'il aurait fait sans conflits. Il semble que ce git addn'est pas nécessaire; bien que cela puisse être utile, vous avez de nombreux fichiers avec des conflits. Au fur et à mesure que chacun est résolu , ils peuvent être ajoutés et les git statusgarder en trace.
bruit sans art
13

Si, comme moi, ce que vous voulez habituellement, c'est remplacer le contenu du répertoire de travail par celui des fichiers cachés et que vous obtenez toujours un conflit, alors ce que vous voulez, c'est résoudre le conflit à l'aide git checkout --theirs -- .de la racine.

Après cela, vous pouvez git resetapporter toutes les modifications de l'index au répertoire de travail, car apparemment en cas de conflit, les modifications apportées aux fichiers non en conflit restent dans l'index.

Vous pouvez également vouloir exécuter git stash drop [<stash name>]après, pour vous débarrasser de la cachette, car git stash popne la supprimez pas en cas de conflits.

Pedro Gimeno
la source
2

Notez que Git 2.5 (T2 2015) un futur Git pourrait essayer de rendre ce scénario impossible.

Voir commit ed178ef par Jeff King ( peff), 22 avril 2015.
(Fusionné par Junio ​​C Hamano - gitster- dans commit 05c3967 , 19 mai 2015)

Remarque: cela a été annulé. Voir ci-dessous .

stash: nécessite un index propre pour appliquer / pop

Problème

Si vous avez organisé le contenu de votre index et exécuté " stash apply/pop", nous pouvons rencontrer un conflit et placer de nouvelles entrées dans l'index.
La restauration de votre état d'origine est difficile à ce stade, car des outils tels que "git reset --keep" effaceront tout ce qui est mis en scène .

En d'autres termes:

" git stash pop/apply" J'ai oublié de m'assurer que non seulement l'arbre de travail est propre, mais aussi que l'index est propre.
Ce dernier est important car une application cachée peut entrer en conflit et l'index sera utilisé pour la résolution des conflits.

Solution

Nous pouvons rendre cela plus sûr en refusant de postuler lorsqu'il y a des changements par étapes.

Cela signifie que s'il y avait des fusions avant en raison de l'application d'une stash sur des fichiers modifiés (ajoutés mais non validés), maintenant, il n'y aurait plus de fusion car la stash apply / pop s'arrêterait immédiatement avec:

Cannot apply stash: Your index contains uncommitted changes.

Vous forcer à valider les modifications signifie qu'en cas de fusion, vous pouvez facilement restaurer l'état initial (avant git stash apply/pop) avec a git reset --hard.


Voir commit 1937610 (15 juin 2015) et commit ed178ef (22 avril 2015) par Jeff King ( peff) .
(Fusionné par Junio ​​C Hamano - gitster- dans commit bfb539b , 24 juin 2015)

Cette validation était une tentative d'améliorer la sécurité de l'application d'une stash, car le processus d'application peut créer des entrées d'index en conflit, après quoi il est difficile de restaurer l'état d'index d'origine.

Malheureusement, cela bloque certains workflows courants autour de " git stash -k", comme:

git add -p       ;# (1) stage set of proposed changes
git stash -k     ;# (2) get rid of everything else
make test        ;# (3) make sure proposal is reasonable
git stash apply  ;# (4) restore original working tree

Si vous "git commit" entre les étapes (3) et (4), alors cela fonctionne. Cependant, si ces étapes font partie d'un hook de pré-validation, vous n'avez pas cette possibilité (vous devez restaurer l'état d'origine, que les tests aient réussi ou échoué).

VonC
la source