Je suis nouveau sur git et je ne sais pas trop comment fonctionne le stashing.
Disons que je travaille sur le maître de branche et essayons de git pull
et je reçois l'erreur que mes modifications locales seront écrasées et devront être cachées ou validées. Si je n'ai effectué aucune de mes modifications et exécuté git stash
, puis faites un git pull
et et mettre à jour avec succès, que se passe-t-il lorsque je git stash apply
?
En général, si quelqu'un d'autre modifie des fichiers et que j'exécute git pull
, que se passe-t-il quand je run git stash apply
? cela écrase-t-il les fichiers qui viennent d'être mis à jour, qu'ils aient été mis en scène ou non lorsque je les ai cachés? Est-ce qu'il écrase tous les fichiers que je viens de mettre à jour avec git pull
, avec les fichiers qui ont été cachés?
git stash
bloque un stash-bag - c'est une forme particulière de commit de fusion qui n'est sur aucune branche - sur le commit HEAD
en cours. A plus tard git stash apply
, lorsque vous êtes à n'importe quel commit - probablement un différent commit - puis essaie de restaurer les changements git calcule en regardant à la fois la cachette suspendue- sac et le commit dont il est suspendu.
Lorsque vous avez terminé les modifications, vous devez utiliser git stash drop
pour lâcher le sac caché du commit, il était "caché". (Et, git stash pop
est juste un raccourci pour "appliquer, puis supprimer automatiquement". Je recommande de garder les deux étapes séparées, cependant, au cas où vous n'aimeriez pas le résultat de "appliquer" et que vous souhaitez réessayer plus tard.)
git stash
est en fait assez complexe.
Il a été dit que "git a beaucoup plus de sens une fois que vous comprenez X" , pour de nombreuses valeurs différentes de "X", qui se généralise à "git a beaucoup plus de sens une fois que vous comprenez git". :-)
Dans ce cas, pour vraiment comprendre stash
, vous devez comprendre comment les commits, les branches, l'index/staging-area, l'espace de nom de référence de git et fusionnent tout le travail, car git stash
crée un commit de fusion très particulier auquel se réfère un nom en dehors des espaces de noms habituels - un type de fusion étrange qui n'est pas du tout "sur une branche" - et git stash apply
utilise le mécanisme de fusion de git pour tenter de "réappliquer" les modifications enregistrées lorsque la validation de fusion particulière a été effectuée, en conservant éventuellement la distinction entre les modifications par étapes et non par étapes.
Heureusement , vous n'avez pas vraiment besoin de comprendre tout cela pour utilisergit stash
.
Ici, vous travaillez sur une branche (master
) et vous avez des modifications qui ne sont pas encore prêtes, donc vous ne voulez pas les valider sur la branche.1 Pendant ce temps, quelqu'un d'autre a mis quelque chose de bien - ou du moins, vous espérez que c'est bon - dans le Origin/master
sur le repo distant, vous voulez donc les récupérer.
Disons que vous et eux avez tous les deux commencé avec des commits qui se terminent par - A - B - C
, c'est-à-dire que C
est la validation finale que vous aviez dans votre référentiel lorsque vous avez commencé à travailler sur la branche master
. Le nouveau "quelque chose de bien" est validé, nous appellerons D
et E
.
Dans votre cas, vous exécutez git pull
et il échoue avec le problème "répertoire de travail non nettoyé". Donc, vous exécutez git stash
. Cela engage vos trucs pour vous, dans sa mode cachette bizarre spéciale, de sorte que votre répertoire de travail est maintenant propre. Maintenant vous pouvez git pull
.
En termes de dessin de commits (un graphique comme vous obtenez avec gitk
ou git log --graph
), vous avez maintenant quelque chose comme ça. La cachette est le petit sac de -i-w
pendant la validation sur laquelle vous étiez "dans", dans votre branche master
, lorsque vous avez exécuté git stash
. (La raison des noms i
et w
est qu'il s'agit des parties "i" ndex/staging-area et "w" ork-tree de la réserve.)
- A - B - C - D - E <-- HEAD=master, Origin/master
|\
i-w <-- the "stash"
Ce dessin est ce que vous obtenez si vous avez commencé à travailler sur master
et que vous n'avez jamais fait any commits. Le dernier commit que vous aviez était donc C
. Après avoir fait la cachette, git pull
a pu ajouter des commits D
et E
à votre branche locale master
. Le sac de travail caché est toujours suspendu C
.
Si vous avez fait vous-même quelques commits - nous les appellerons Y
, pour votre commit, et Z
juste pour avoir deux commits - le résultat du "stash then pull" ressemble à cette:
.-------- Origin/master
- A - B - C - D - E - M <-- HEAD=master
\ /
Y - Z
|\
i-w <-- the "stash"
Cette fois, après que stash
ait suspendu son sac de rangement Z
, le pull
- qui est juste fetch
puis merge
- a dû faire une vraie fusion, au lieu d'une simple "avance rapide". Il fait donc commit M
, le commit de fusion. Le Origin/master
l'étiquette fait toujours référence à la validation E
, pas à M
. Vous êtes maintenant sur master
à la validation M
, qui est une fusion de E
et Z
. Vous avez "une longueur d'avance" sur Origin/master
.
Dans les deux cas, si vous exécutez maintenant git stash apply
, le script caché (c'est un script Shell qui utilise beaucoup de commandes de plomberie git de bas niveau) efficacement2 est ce que ca:
git diff stash^ stash > /tmp/patch
git apply /tmp/patch
Cela diffère stash
, qui nomme w
- la partie "arbre de travail" de la cachette - par rapport à la bonne3 parent. En d'autres termes, il découvre "ce que vous avez changé" entre la validation parent appropriée (C
ou Z
, selon le cas) et l'arborescence de travail cachée. Il applique ensuite les modifications à la version actuellement extraite, qui est soit E
ou M
, toujours selon l'endroit où vous avez commencé.
Par ailleurs, git stash show -p
exécute vraiment le même git diff
commande (sans > /tmp/patch
partie bien sûr). Sans pour autant -p
, il exécute le diff avec --stat
. Donc, si vous voulez voir en détail ce que git stash apply
fusionnera, utilisez git stash show -p
. (Cela ne vous montrera pas ce que git stash apply
peut cependant tenter d'appliquer depuis la partie index de la cachette; c'est un petit reproche que j'ai avec le script caché.)
Dans tous les cas, une fois que la cachette s'applique correctement, vous pouvez utiliser git stash drop
pour supprimer la référence au sac de rangement, afin qu'il puisse être récupéré. Tant que vous ne le déposez pas, il porte un nom (refs/stash
, alias stash@{0}
) donc ça reste "pour toujours" ... sauf pour le fait que si vous faites une cachette nouveau, le script stash
"pousse" la cachette courante dans le stash reflog (pour que son nom devienne stash@{1}
) et utilise la nouvelle cachette refs/stash
Nom. La plupart des entrées de reflog restent pendant 90 jours (vous pouvez configurer cela différemment), puis expirent. Les stashes n'expirent pas par défaut, mais si vous le configurez autrement, un stash "poussé" peut être perdu, alors faites attention à ne pas "sauvegarder pour toujours" si vous commencez à configurer git à votre convenance.
Notez que git stash drop
"fait apparaître" la pile de dissimulations ici, renumérotant stash@{2}
à stash@{1}
et faire stash@{1}
devient simple stash
. Utilisation git stash list
pour voir la pile de sauvegarde.
1Ce n'est pas mal d'aller de l'avant et de les engager de toute façon, puis de faire plus tard git rebase -i
pour écraser ou corriger les deuxième, troisième, quatrième, ..., nième commits et/ou réécrire la validation temporaire du "point de contrôle". Mais cela est indépendant de cela.
2C'est un peu plus complexe car vous pouvez utiliser --index
pour essayer de conserver les modifications par étapes, mais en fait, si vous regardez dans le script, vous verrez la séquence de commandes réelle git diff ... | git apply --index
. Il suffit vraiment d'appliquer un diff, dans ce cas! Finalement, il invoque git merge-recursive
directement, cependant, pour fusionner dans l'arborescence de travail, permettant aux mêmes changements d'avoir été apportés d'ailleurs. Un simple git apply
échouerait si votre correctif faisait quelque chose que les "bonnes choses" commettaient également D
et E
.
3Cela utilise la syntaxe magique de nommage parent de git, avec une petite planification préalable à l'intérieur du script stash
. Étant donné que la cachette est ce commit de fusion génial, w
a deux ou même trois parents, mais le script de stash la configure de sorte que le "premier parent" soit le commit d'origine, C
ou Z
, selon le cas. Le "deuxième parent" stash^2
est l'état de l'index au moment de la validation, affiché comme i
dans le petit sac de rangement suspendu, et le "troisième parent", s'il existe, est des fichiers non mis en scène et peut-être ignorés, de git stash save -u
ou git stash save -a
.
Notez que je suppose, dans cette réponse, que vous avez pas soigneusement mis en scène une partie de votre arbre de travail et que vous êtes pas en utilisant git stash apply --index
pour restaurer l'index par étapes. En ne faisant rien de tout cela, vous rendez le commit i
à peu près redondant, de sorte que nous n'avons pas à nous en soucier pendant l'étape apply
. Si vous êtes en utilisant apply --index
ou équivalent, et ayez les éléments mis en scène, vous pouvez entrer dans beaucoup plus de cas d'angle, où la cachette ne s'appliquera pas proprement.
Ces mêmes mises en garde s'appliquent, avec encore plus de cas d'angle, aux stashes enregistrés avec -u
ou -a
, qui ont ce troisième commit.
Pour ces cas extra-durs, git stash
fournit un moyen de transformer une cachette en une véritable branche - mais je laisse tout cela à une autre réponse.
la commande stash git se souvient d'où vient la cachette:
git stash list
production
stash@{0}: WIP on master.color-rules.0: 35669fb [NEW] another step toward initial cube
Où vous pouvez voir sur quel SHA1 il a été fabriqué. Donc, si vous git stash, git pull, git stash s'appliquent et que vous avez un conflit, le stash n'est pas supprimé (il ne le sera que si vous le supprimez ou si l'application a réussi). Ainsi, vous pouvez toujours obtenir le SHA1 à partir de la liste de sauvegarde git et
git checkout 35669fb
git stash apply
et il est garanti de travailler. Je recommande d'utiliser l'option -b et de fournir un nom de branche pour cette récupération.
Cela étant dit, mon flux de travail préféré est de TOUJOURS régler sous un nouveau nom "personnel" pour éviter de tels problèmes
Les modifications généralement non validées sont toujours mauvaises. Soit vos modifications sont bonnes, puis validez-les, soit elles sont mauvaises que de les supprimer. Faire des opérations git tout en ayant des modifications non validées a tendance à causer des problèmes et git ne pourra pas vous aider, car git ne sait rien de ce que vous n'avez pas commis.
Cela dit, revenons à votre question. ;)
Git est généralement assez intelligent. Lorsque vous appliquez votre stash, il essaie de fusionner vos modifications avec les autres modifications. La plupart du temps, cela fonctionne.
Si les changements entrent vraiment en conflit, parce que vous avez changé les mêmes lignes d'une manière différente, git vous le dira et vous devrez résoudre le conflit par vous-même. - Même dans ce cas, git vous aidera en ayant git mergetool
, qui lancera une commande appropriée pour vous montrer les conflits et vous permettra de les résoudre un par un.