git stash et appliquer

je suis nouveau à git et pas tout à fait clair sur la façon dont stashing fonctionne.

disons que je travaille sur branch master et essaye de git pull et de recevoir l'erreur que mes modifications locales seraient écrasées et doivent être cachées ou commises. Si je n'ai pas mis en scène l'un de mes changements et exécuter git stash , puis faire 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 je cours git pull , que se passe-t-il quand je run git stash apply ? est-ce qu'il réécrit les fichiers qui viennent d'être mis à jour, qu'ils aient été ou non mis en scène Quand je les ai planqués? Est-ce qu'il écrase tous les fichiers que je viens de mettre à jour avec git pull , avec les fichiers qui ont été planqués?

24
demandé sur Alexis King 2013-12-06 00:20:13

3 réponses

Rapide "TL;DR" take-away version, donc on peut revenir plus tard, et l'étude plus

git stash accroche un Stash-bag-c'est une forme particulière d'une commit de fusion qui n'est sur aucune branche-sur le courant HEAD commit. Un plus tard git stash apply , quand vous êtes à n'importe quel commit-probablement un différent commit-puis tente de restaurer le change git calcule en regardant à la fois la cachette-sac et le s'engager, il se bloque.

quand vous avez terminé avec les changements, vous devez utiliser git stash drop pour laisser tomber le sac de la commit il a été "planqué sur". (Et, git stash pop est juste un raccourci pour "appliquer, puis déposer automatiquement". Je recommande de garder les deux étapes séparées, cependant, dans le cas où vous n'aimez pas le résultat de "Appliquer" et vous voulez essayer à nouveau plus tard.)

la version longue

git stash est en fait assez complexe.

il a été dit que " git fait beaucoup plus de sens une fois que vous comprenez X " , pour de nombreuses valeurs différentes de" X", qui se généralise à"git fait beaucoup plus de sens une fois que vous comprenez git". :- )

dans ce cas, à vraiment comprendre stash , vous devez comprendre comment commet, branches, l'index / zone de mise en scène, l'espace de référence de référence de git, et fusionne tous les travaux, parce que git stash crée une commit de fusion très particulière qui est référencée par un nom en dehors des espaces-noms habituels-une sorte bizarre de fusion qui n'est pas "sur une branche" du tout-et git stash apply utilise la machine de fusion de git pour tenter de "ré-appliquer" les changements sauvegardés lorsque la commit de fusion particulière a été faite, en préservant optionnellement la distinction entre les changements échelonnés et non-étalés.

Heureusement , vous n'avez pas besoin de comprendre tout cela utiliser git stash .

ici, vous travaillez sur une branche ( master ) et vous avez quelques changements qui ne sont pas encore prêts, donc vous ne voulez pas les engager sur la branche. 1 pendant ce temps, quelqu'un d'autre a mis quelque chose de bon-ou du moins, vous espérez qu'il est bon-dans le origin/master plus sur le repo à distance, donc vous voulez ramasser ceux-ci.

disons que vous et eux avez commencé par commettre cette fin en - A - B - C , i.e., C est le dernier engagement que vous aviez dans votre pension lorsque vous avez commencé à travailler sur la branche master . Le nouveau" quelque chose de bon "engage, nous appellerons D et E .

dans votre cas, vous lancez git pull et cela échoue avec le problème" répertoire de travail non propre". Alors, vous lancez git stash . Cela engage vos affaires pour vous, dans sa manière étrange cachette-y, 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 ceci. La cachette est le petit sac-de - i-w suspendu hors du commit vous étiez "sur", dans votre master branche, quand vous avez couru 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 cachette.)

- 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 n'a jamais fait tout commits. Le dernier engagement que vous avez eu est donc C . Après avoir fait la cachette, git pull a pu ajouter commits D et E à votre branche locale master . Le sac de travail caché est toujours suspendu au loin C .

si vous avez fait quelques commissions de votre own-nous allons les appeler Y , pour votre commit, et Z juste pour avoir deux commits-le résultat du "stash puis pull" ressemble à ceci:

                   .-------- origin/master
- A - B - C - D - E - M  <-- HEAD=master
            \       /
              Y - Z
                  |\
                  i-w    <-- the "stash"

cette fois, après stash accroché sa cachette-sac off Z , le pull - qui est juste fetch puis merge - a dû faire une vraie fusion, au lieu de juste un"fast forward". Il fait donc commit M , la commit de fusion. Le label origin/master fait toujours référence à commit E , pas M . Vous êtes maintenant sur master à commit M , qui est une fusion de E et Z . Vous avez une longueur d'avance sur origin/master .

dans les deux cas, si vous lancez maintenant git stash apply , le script stash (c'est un script shell qui utilise beaucoup de bas niveau git "plumbing" commandes) effectivement 2 fait ceci:

git diff stash^ stash > /tmp/patch
git apply /tmp/patch

Cette diff stash , dont les noms w -la partie "arbre de travail" de la cachette-contre la correcte 3 parent. En d'autres termes , il découvre "ce que vous avez changé" entre le parent approprié commit ( C ou Z , selon le cas) et l'arbre de travail caché. Il applique ensuite les modifications à la version actuellement cochée, qui est soit E ou M , encore une fois en fonction de l'endroit où vous avez commencé.

incidemment, git stash show -p ne fait que courir ce même git diff commande (avec NO > /tmp/patch partie de cours). Sans -p , il exécute la diff avec --stat . Donc, si vous voulez voir en détail ce que git stash apply va fusionner, utilisez git stash show -p . (Ceci ne vous montrera pas ce que git stash apply peut essayer d'appliquer à partir de la partie index de la cachette, cependant; c'est une plainte mineure que j'ai avec le script de cachette.)


dans tous les cas, une fois que la cachette s'applique proprement, vous pouvez utiliser git stash drop pour supprimer la référence au sac-poubelle, afin qu'il puisse être ramassé. Jusqu'à ce que vous le laissiez tomber, il a un nom ( refs/stash , alias stash@{0} ) donc il reste autour de "pour toujours" ... excepté le fait que si vous faites un nouveau stash, le stash script "push" La stash actuelle dans le réflog stash (de sorte que son nom devient stash@{1} ) et fait la nouvelle stash utiliser le refs/stash nom. La plupart des entrées reflog restent autour de 90 jours (vous pouvez configurer cet être différent), puis expirer. Les planques n'expirent pas par défaut, mais si vous configurez cela autrement, une planque "poussée" peut se perdre, alors faites attention en fonction de "save forever" si vous commencez à configurer git à votre convenance.

notez que git stash drop "pops" the stash stack stack here, renuméroter stash@{2} en stash@{1} et en faisant stash@{1} " become plain stash . Utilisez git stash list pour voir la Stash-stack.


1 il n'est pas mauvais d'aller de l'avant et de les commettre de toute façon, et puis faire un plus tard git rebase -i pour écraser ou réparer plus loin deuxième, troisième, quatrième, ..., nth s'engage, et/ou de réécriture de la temporaire de "point de contrôle" s'engager. Mais c'est indépendant de ce.

2 il est assez un peu plus complexe parce que vous pouvez utiliser --index pour essayer de garder les changements mis en scène, mais en fait, si vous regardez dans le script, vous verrez la séquence de commande actuelle git diff ... | git apply --index . Il ne fait vraiment appliquer une diff, dans ce cas! Finalement il invoque git merge-recursive directement, cependant, pour fusionner dans l'arbre de travail, permettant les mêmes changements d'avoir été apportés d'ailleurs. Un simple git apply échouerait si votre patch faisait quelque chose que le "good stuff" commit D et E fait aussi.

3 cela utilise le nom de parent de git syntaxe magique, avec un peu de planification à l'intérieur du script stash . Parce que la stash est ce funky commit de fusion, w a deux ou même trois parents, mais le script de stash le met en place de sorte que le "premier parent" est le commit original, C ou Z , selon le cas. Le" second parent " stash^2 est l'état index au moment de la propagation, représenté par i dans la petite cachette suspendue, et le "troisième parent", s'il existe, est fichiers non classés 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 travail-de l'arbre et que vous êtes pas à l'aide git stash apply --index pour restaurer la mise en scène de l'index. En ne faisant rien de tout cela, vous rendez le i à peu près redondant, de sorte que nous n'avons pas à nous en inquiéter pendant l'étape apply . Si vous sont en utilisant apply --index ou l'équivalent, et ont articles mis en scène, vous pouvez entrer dans beaucoup plus de cas de coin, où la cachette ne s'applique pas proprement.

ces mêmes mises en garde s'appliquent, avec encore plus de cas de coin, aux caches sauvegardées avec -u ou -a , qui ont cette troisième propagation.

pour ces cas extra-durs, git stash fournit un moyen de transformer une cachette en une véritable branch - mais je vais laisser tout cela à une autre réponse.

54
répondu torek 2017-09-19 17:33:20

généralement, les changements non engagés sont toujours mauvais. Soit vos changements sont bons, puis les commettre, ou ils sont mauvais que de les jeter. Faire des opérations git tout en ayant des changements non engagés tend à causer des problèmes et git ne sera pas en mesure de 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 cachette, il tente de fusionner votre les changements avec les autres changements. La plupart du temps cela fonctionne, tout simplement.

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 permet de les résoudre un par un.

1
répondu michas 2013-12-05 20:31:19

la commande git stash se souvient où la cachette vient de:

   git stash list

sortie

   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 vous avez un conflit, la stash n'est pas abandonné (il ne sera que si vous déposez ou si la demande a été réussie). Donc vous pouvez toujours obtenir le SHA1 de git stash list et

   git checkout 35669fb
   git stash apply

et il est garanti pour fonctionner. Je recommander l'utilisation de l'option-b et fournir le nom de la direction pour ce rétablissement.

cela dit, mon workflow préféré est de toujours vérifier sous nouveau nom" personnel "pour éviter de tels problèmes

1
répondu Sebastien 2013-12-05 20:37:12