149 votes

Pourquoi git stash pop indique-t-il qu'il n'a pas pu restaurer les fichiers non suivis de l'entrée stash ?

J'avais un tas de modifications staged et unstaged et je voulais passer rapidement à une autre branche, puis revenir.

J'ai donc mis en scène mes changements en utilisant :

$ git stash push -a

(En rétrospective, j'aurais probablement pu utiliser --include-untracked au lieu de --all )

Ensuite, quand j'ai voulu ouvrir la cachette, j'ai eu un tas d'erreurs du genre :

$ git stash pop
foo.txt already exists, no checkout
bar.txt already exists, no checkout
...
Could not restore untracked files from stash entry

Il semble qu'il n'y ait pas de changements restaurés à partir de la réserve.

J'ai aussi essayé $ git stash branch temp mais cela montre les mêmes erreurs.

J'ai trouvé un moyen de contourner ce problème en utilisant :

$ git stash show -p | git apply

Le désastre est évité pour l'instant mais cela soulève quelques questions.

Pourquoi cette erreur s'est-elle produite en premier lieu et comment puis-je l'éviter la prochaine fois ?

60 votes

J'ai dû utiliser : git stash show -p | git apply --3

7 votes

La seule chose qui a fonctionné pour moi est ABOVE COMMENTAIRE !!

4 votes

Merci @xmedeko, quelqu'un peut-il faire la différence entre git stash show -p | git apply et git stash show -p | git apply --3 ?

149voto

Daniel Smith Points 676

J'ai réussi à recréer votre problème. Il semble que si vous cachez des fichiers non suivis et que vous créez ensuite ces fichiers (dans votre exemple, foo.txt et bar.txt ), alors vous avez des modifications locales sur des fichiers non suivis qui seraient écrasées lorsque vous appliquez la méthode git stash pop .

Pour contourner ce problème, vous pouvez utiliser la commande suivante :

git checkout stash -- .

Ceci remplacera toutes les modifications locales non sauvegardées, soyez prudent. Voici quelques informations complémentaires que j'ai trouvées sur la commande précédente .

0 votes

Mon arbre de travail était définitivement propre bien qu'il ait pu y avoir des changements dans les fichiers ignorés.

1 votes

On dirait qu'en utilisant --all / -a inclura les fichiers ignorés donc cela peut être pertinent.

2 votes

Je vais supposer que la même logique s'applique aux fichiers ignorés et marquer ceci comme étant la réponse (bien que je pense que l'option git merge --squash --strategy-option=theirs stash est préférable dans ce cas).

143voto

torek Points 25463

À titre d'explication supplémentaire, notez que git stash fait soit deux engagements, soit trois engagements. La valeur par défaut est deux ; vous obtenez trois si vous utilisez n'importe quelle orthographe de l'option --all ou --include-untracked options.

Ces deux, ou trois, commits sont spéciaux d'une manière importante : ils sont sur pas de branche. Git les localise grâce au nom spécial stash . 1 La chose la plus importante, cependant, est ce que Git vous permet - et fait que vous faites avec ces deux ou trois engagements. Pour comprendre cela, nous devons regarder ce qu'il y a dans ces commits.

Ce qu'il y a dans une cachette

Chaque commit peut lister un ou plusieurs parent s'engage. Ceux-ci forment un graphe, où les commits ultérieurs pointent vers les précédents. La cachette contient normalement deux commits, que j'aime appeler i pour l'index / le contenu de la zone de mise en scène, et w pour le contenu de l'arbre de travail. Rappelez-vous également que chaque livraison contient un instantané. Dans un commit normal, cet instantané est réalisé de l'index / le contenu de la zone de mise en scène. Ainsi, le i est en fait un commit parfaitement normal ! Il n'est juste sur aucune branche :

...--o--o--o   <-- branch (HEAD)
           |
           i

Si vous faites une cachette normale, les git stash Le code fait w maintenant en copiant tous vos fichiers d'arbre de travail suivis (dans un index auxiliaire temporaire). Git définit le premier parent de ce w pour pointer vers le HEAD commit, et le second parent pour pointer vers commit i . Enfin, il fixe stash pour pointer vers ce w commettre :

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash

Si vous ajoutez --include-untracked ou --all Git fait un commit supplémentaire, u entre la fabrication i et w . Le contenu de l'instantané pour u sont les fichiers qui ne sont pas suivis mais qui ne sont pas ignorés ( --include-untracked ), ou des fichiers qui ne sont pas suivis même s'ils sont ignorés ( --all ). Ce supplément u commettre a pas de parent, et ensuite quand git stash fait w il fixe w 's troisième parent à ce u s'engager, pour que vous obteniez :

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash
            /
           u

Git aussi, à ce stade, supprime tous les fichiers de l'arbre de travail qui se sont retrouvés dans les u commettre (en utilisant git clean pour le faire).

Restaurer une cachette

Quand vous allez à rétablir une réserve, vous avez la possibilité d'utiliser --index ou de ne pas l'utiliser. Ceci indique git stash apply (ou n'importe quelle commande qui utilise en interne la commande apply tels que pop ) qu'il devrait utiliser le site i pour tenter de modifier votre index actuel. Cette modification se fait avec :

git diff <hash-of-i> <hash-of-i's-parent> | git apply --index

(plus ou moins ; il y a un tas de détails qui entravent l'idée de base).

Si vous omettez --index , git stash apply ignore complètement le i commettre.

Si la cachette n'a que deux commits, git stash apply peut maintenant appliquer le w s'engager. Pour ce faire, il appelle git merge 2 (sans lui permettre de commiter ou de traiter le résultat comme une fusion normale), en utilisant le commit original sur lequel la cachette a été faite ( i et w (le premier parent de l'enfant) comme base de fusion, w comme le --theirs et votre livraison actuelle (HEAD) comme cible de la fusion. Si la fusion réussit, tout va bien - enfin, au moins Git le pense - et le git stash apply elle-même réussit. Si vous avez utilisé git stash pop pour appliquer la cachette, le code maintenant gouttes la cachette. 3 Si la fusion échoue, Git déclare que l'application a échoué. Si vous avez utilisé git stash pop le code conserve la cachette et fournit le même état d'échec que dans le cas de git stash apply .

Mais si vous avez ce troisième commettre - s'il y a un u engager dans la réserve que vous appliquez - alors les choses changent ! Il n'est pas possible de prétendre que le u commit n'existe pas. 4 Git insiste pour extraire tous les fichiers de que u dans l'arbre de travail actuel. Cela signifie que les fichiers doivent soit ne pas exister du tout, soit avoir le même contenu que dans l'arbre de travail actuel. u commettre.

Pour ce faire, vous pouvez utiliser git clean mais rappelez-vous que les fichiers non suivis (ignorés ou non) n'ont aucune autre existence dans un dépôt Git, donc assurez-vous que ces fichiers peuvent tous être détruits ! Ou bien, vous pouvez créer un répertoire temporaire, et y déplacer les fichiers pour les mettre en sécurité - ou même faire un autre git stash save -u ou git stash save -a puisque ceux-ci fonctionneront git clean pour vous. Mais ça vous laisse juste avec un autre u -pour s'en occuper plus tard.


1 C'est en fait refs/stash . Ceci est important si vous créez une branche nommée stash le nom complet de la branche est refs/heads/stash Il n'y a donc pas de conflit entre les deux. Mais ne faites pas ça : Git Cela ne vous dérangera pas, mais vous vous embrouillerez vous-même :-)

2 Le site git stash utilise en fait git merge-recursive directement ici. Ceci est nécessaire pour de multiples raisons, et a également pour effet secondaire de s'assurer que Git ne le traite pas comme une fusion lorsque vous résolvez les conflits et livrez.

3 C'est pourquoi je recommande d'éviter git stash pop en faveur de git stash apply . Vous avez l'occasion de revoir ce qui a été appliqué et de décider si c'était en fait appliqué correctement. Si ce n'est pas le cas, vous vous avez toujours votre stock ce qui signifie que vous pouvez utiliser git stash branch pour tout récupérer parfaitement. Eh bien, en supposant l'absence de ce pesant u commettre.

4 Il devrait vraiment y en avoir : git stash apply --skip-untracked ou autre. Il devrait également y avoir une variante qui signifie laissez tomber tous ces u commettre des fichiers dans un nouveau répertoire par exemple, git stash apply --untracked-into <dir> peut-être.

3 votes

Wow ! Vous méritez une médaille ! Y a-t-il un livre que vous recommanderiez pour des explications aussi détaillées de git ? C'est génial !

3 votes

@seelts Malheureusement, Git est en constante évolution, donc les livres deviennent rapidement obsolètes. Mais Git doit être compris comme un ensemble d'outils - des commandes qui manipulent un commit-full de fichiers, ou qui manipulent le graphe de commit, ou quoi que ce soit d'autre - que vous assemblez en tout ce qui est utile à l'utilisateur. vous et peu de livres semblent l'aborder de cette manière.

3 votes

Je ne comprends pas la solution au problème. Faut-il simplement ajouter --index : git stash apply --index ?

39voto

Erik Koopmans Points 676

Pour développer Réponse de Daniel Smith : ce code ne rétablit que le traqué même si vous avez utilisé --include-untracked (ou -u ) lors de la création de la réserve. Le code complet requis est le suivant :

git checkout stash -- .
git checkout stash^3 -- .
git stash drop

# Optional to unstage the changes (auto-staged by default).
git reset

Cela permet de restaurer entièrement le contenu de la piste (en stash ) et les contenus non suivis (dans stash^3 ), puis supprime la cachette. Quelques notes :

  • Soyez prudent. - cela va tout écraser avec le contenu de votre cachette !
  • Restaurer les fichiers avec git checkout fait en sorte qu'ils soient tous mis en scène automatiquement, j'ai donc ajouté git reset pour tout déstabiliser.
  • Certaines ressources utilisent stash@{0} et stash@{0}^3 Dans mes essais, cela fonctionne de la même manière avec ou sans @{0}

Sources :

3 votes

Pourquoi supprimer la cachette, pourquoi ne pas la laisser là pendant un certain temps pour des raisons de sécurité ?

0 votes

Danijel Bien sûr, vous pouvez garder la réserve - cela dépend de votre cas d'utilisation, je suppose. Dans mon cas, j'en ai fini avec la réserve une fois qu'elle a été restaurée.

7voto

Sanjay Nishad Points 784

À part d'autres réponses, j'ai fait un petit truc

  • Suppression de tous les nouveaux fichiers (fichiers déjà existants, par exemple foo.txt et bar.txt dans la question)
  • git stash apply (peut utiliser n'importe quelle commande, par exemple appliquer, pop, etc.)

1 votes

Cela ne fonctionne pas les fichiers supprimés apparaissent à nouveau dans la cachette et l'erreur aussi !

0voto

user2932782 Points 11

Je viens de rencontrer ce problème. Comme les réponses ci-dessus ne fonctionnaient pas sans perdre des fichiers de la cachette, j'ai improvisé.

Mon stock, réalisé avec git stash -u avait dépisté les fichiers nouvellement ajoutés. Certains de ces fichiers provenaient de la génération de code, et ont été livrés séparément à la branche master avant que je n'essaie d'ouvrir la cachette. Lorsque j'ai ouvert ma cachette, j'ai obtenu le résultat suivant :

> git stash pop
web-app/src/app/rest-services/api/models/model1.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model2.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model3.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model4.ts already exists, no checkout
error: could not restore untracked files from stash

En partant du principe que git ne pouvait pas fusionner 2 nouveaux fichiers différents, j'ai supprimé chacun des fichiers nommés dans mon répertoire de travail. J'ai ensuite été en mesure d'appliquer l'ensemble de la cachette sans fichiers manquants ou erreurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X