Comme explication supplémentaire, notez que cela git stash
fait 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 --all
ou --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 i
pour le contenu de l'index / de la zone de préparation et w
pour 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 i
commit 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 stash
code le crée w
maintenant en copiant tous vos fichiers d'arborescence de travail suivis (dans un index auxiliaire temporaire). Git définit le premier parent de ce w
commit pour qu'il pointe vers le HEAD
commit et le second parent pour pointer vers commit i
. Enfin, il se met stash
à pointer vers ce w
commit:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Si vous ajoutez --include-untracked
ou --all
, Git effectue un commit supplémentaire,, u
entre la création de i
et w
. Le contenu de l'instantané pour u
sont 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 u
commit supplémentaire n'a pas de parent, puis quand il est git stash
fait w
, il définit w
le troisième parent de ce u
commit, 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 u
commit (en utilisant git clean
pour faire cela).
Restaurer une cachette
Lorsque vous allez restaurer une réserve, vous avez la possibilité de l'utiliser --index
ou 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 i
validation 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 apply
ignore complètement le i
commit.
Si le stash n'a que deux validations , vous git stash apply
pouvez maintenant appliquer le w
commit. Il le fait en appelant git merge
2 (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éé ( i
le parent de et w
le premier parent de) comme base de fusion, w
comme le --theirs
commit 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 apply
lui - même réussit. Si vous aviez l'habitude git stash pop
d'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 u
commit dans la réserve que vous appliquez - alors les choses changent! Il n'y a aucune option pour prétendre que le u
commit n'existe pas. 4 Git insiste pour extraire tous les fichiers de ce u
commit, 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 u
commit.
Pour que cela se produise, vous pouvez utiliser git clean
vous-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 -u
ou git stash save -a
, puisque ceux-ci seront exécutés git clean
pour vous. Mais cela ne vous laisse qu'une autre u
ré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 stash
code utilise en fait git merge-recursive
directement 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 branch
tout récupérer parfaitement. Eh bien, en supposant l'absence de cet u
engagement embêtant .
4 Il devrait vraiment y avoir: git stash apply --skip-untracked
ou quelque chose. Il devrait également y avoir une variante qui signifie déposer tous ces u
fichiers de validation dans un nouveau répertoire , par exemple git stash apply --untracked-into <dir>
, peut-être.
git stash show -p | git apply --3