Comme explication supplémentaire, notez que cela git stashfait soit deux commits, soit trois commits. La valeur par défaut est de deux; vous en obtenez trois si vous utilisez une orthographe des options --allou --include-untracked.
Ces deux, ou trois, commits sont spéciaux d'une manière importante: ils ne sont sur aucune branche. Git les localise via le nom spécial stash. 1 Le plus important, cependant, est ce que Git vous permet - et vous oblige - à faire avec ces deux ou trois commits. Pour comprendre cela, nous devons examiner le contenu de ces commits.
Qu'y a-t-il dans une cachette
Chaque commit peut lister un ou plusieurs commits parents . Ceux-ci forment un graphique, où les commits ultérieurs renvoient aux précédents. Le stash contient normalement deux commits, que j'aime appeler ipour le contenu de l'index / de la zone de préparation et wpour le contenu de l'arbre de travail. Souvenez-vous également que chaque commit contient un instantané. Dans une validation normale, cet instantané est créé à partir du contenu de l'index / de la zone de préparation. Donc le icommit est en fait un commit parfaitement normal! Ce n'est tout simplement pas sur aucune branche:
...--o--o--o <-- branch (HEAD)
|
i
Si vous créez une réserve normale, le git stashcode le crée wmaintenant en copiant tous vos fichiers d'arborescence de travail suivis (dans un index auxiliaire temporaire). Git définit le premier parent de ce wcommit pour qu'il pointe vers le HEADcommit et le second parent pour pointer vers commit i. Enfin, il se met stashà pointer vers ce wcommit:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Si vous ajoutez --include-untrackedou --all, Git effectue un commit supplémentaire,, uentre la création de iet w. Le contenu de l'instantané pour usont les fichiers qui ne sont pas suivis mais non ignorés ( --include-untracked), ou les fichiers qui ne sont pas suivis même s'ils sont ignorés ( --all). Ce ucommit supplémentaire n'a pas de parent, puis quand il est git stashfait w, il définit wle troisième parent de ce ucommit, de sorte que vous obtenez:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git également, à ce stade, supprime tous les fichiers d'arbre de travail qui se sont terminés dans le ucommit (en utilisant git cleanpour faire cela).
Restaurer une cachette
Lorsque vous allez restaurer une réserve, vous avez la possibilité de l'utiliser --indexou de ne pas l'utiliser. Cela indique git stash apply(ou l'une des commandes qui utilisent en interne apply, telles que pop) qu'il doit utiliser la ivalidation 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 nitty qui entravent l'idée de base ici).
Si vous omettez --index, git stash applyignore complètement le icommit.
Si le stash n'a que deux validations , vous git stash applypouvez maintenant appliquer le wcommit. Il le fait en appelant git merge2 (sans lui permettre de valider ou de traiter le résultat comme une fusion normale), en utilisant le commit d'origine sur lequel le stash a été créé ( ile parent de et wle premier parent de) comme base de fusion, wcomme le --theirscommit et votre commit actuel (HEAD) comme cible de la fusion. Si la fusion réussit, tout va bien - enfin, du moins Git le pense - et le git stash applylui - même réussit. Si vous aviez l'habitude git stash popd'appliquer la réserve, le code supprime maintenant la réserve. 3 Si la fusion échoue, Git déclare que l'application a échoué. Si vous avez utiliségit stash pop, le code conserve la réserve et fournit le même état d'échec que pour git stash apply.
Mais si vous avez ce troisième commit - s'il y a un ucommit dans la réserve que vous appliquez - alors les choses changent! Il n'y a aucune option pour prétendre que le ucommit n'existe pas. 4 Git insiste pour extraire tous les fichiers de ce ucommit, 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 le ucommit.
Pour que cela se produise, vous pouvez utiliser git cleanvous-même - mais rappelez-vous que les fichiers non suivis (ignorés ou non) n'ont pas d'autre existence dans un référentiel Git, alors 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 conserver - ou même en faire un autre git stash save -uou git stash save -a, puisque ceux-ci seront exécutés git cleanpour vous. Mais cela ne vous laisse qu'une autre uréserve de style à gérer plus tard.
1 Ceci est en fait refs/stash. Cela est important si vous créez une branche nommée stash: le nom complet de la branche est refs/heads/stash, donc ceux-ci ne sont pas en conflit. Mais ne faites pas cela: Git ne me dérangerait pas, mais vous vous confondez. :-)
2 Le git stashcode utilise en fait git merge-recursivedirectement ici. Ceci est nécessaire pour plusieurs raisons, et a également pour effet secondaire de s'assurer que Git ne le traite pas comme une fusion lorsque vous résolvez des conflits et commettez.
3 C'est pourquoi je recommande d'éviter git stash pop, en faveur de git stash apply. Vous avez la possibilité de revoir ce qui a été appliqué et de décider si cela a été effectivement appliqué correctement. Sinon, vous avez toujours votre réserve, ce qui signifie que vous pouvez git stash branchtout récupérer parfaitement. Eh bien, en supposant l'absence de cet uengagement embêtant .
4 Il devrait vraiment y avoir: git stash apply --skip-untrackedou quelque chose. Il devrait également y avoir une variante qui signifie déposer tous ces ufichiers de validation dans un nouveau répertoire , par exemple git stash apply --untracked-into <dir>, peut-être.
git stash show -p | git apply --3