Comment puis-je extraire un seul fichier (ou des modifications apportées à un fichier) à partir d'une cachette git?

776

Je voudrais savoir s'il est possible d'extraire un seul fichier ou diff d'un fichier à partir d'une cachette git sans faire sauter le jeu de modifications de la cachette.

Quelqu'un pourrait-il être en mesure de fournir des suggestions / idées à ce sujet?

Danny
la source

Réponses:

1132

Sur la page de manuel de git stash, vous pouvez lire (dans la section "Discussion", juste après la description de "Options") que:

Un stash est représenté comme un commit dont l'arborescence enregistre l'état du répertoire de travail, et son premier parent est le commit chez HEAD lorsque le stash a été créé.

Vous pouvez donc traiter la stash (par exemple, la stash@{0}première / la plus haute) comme un commit de fusion, et utiliser:

$ git diff stash@{0}^1 stash@{0} -- <filename>

Explication: stash@{0}^1signifie le premier parent de la cachette donnée, qui, comme indiqué dans l'explication ci-dessus, est la validation à laquelle les modifications ont été cachées. Nous utilisons cette forme de "git diff" (avec deux commits) car stash@{0}/ refs/stashest un commit de fusion, et nous devons dire à git contre quel parent nous voulons faire la différence. Plus cryptique:

$ git diff stash@{0}^! -- <filename>

devrait également fonctionner (voir la page de manuel git rev-parse pour l'explication de la rev^!syntaxe, dans la section "Spécification des plages").

De même, vous pouvez utiliser git checkout pour extraire un seul fichier de la cachette:

$ git checkout stash@{0} -- <filename>

ou pour l'enregistrer sous un autre nom de fichier:

$ git show stash@{0}:<full filename>  >  <newfile>

ou

$ git show stash@{0}:./<relative filename> > <newfile>

( notez qu'ici <nom de fichier complet> est le chemin d'accès complet d'un fichier par rapport au répertoire supérieur d'un projet (pensez: par rapport à stash@{0})).


Vous devrez peut-être vous protéger stash@{0}de l'expansion du shell, c'est-à-dire utiliser "stash@{0}"ou 'stash@{0}'.

Jakub Narębski
la source
8
C'est plutôt cool ... Je n'ai pas vraiment compris comment fonctionnait la cachette avant d'avoir lu votre réponse (ce qui m'a amené à l'ajout de git-checkout). Je n'ai vraiment pas compris que, lorsque vous faites une stash, git enregistre DEUX validations - une pour l'état de l'index et une pour l'état de la copie de travail qui est une fusion entre l'index et la tête d'origine. Cela explique les arbres étranges que j'ai vus lorsque je visualise le référentiel avec "gitk --all" lorsque des stashes sont présents.
Pat Notz
4
Surtout, je trouve que l'application git checkout est le meilleur moyen d'accomplir ce que je voulais faire. Cependant, j'étais curieux et j'ai vérifié git checkoutla page de manuel de. Il ne peut pas déposer le fichier dans un autre emplacement. Il y a une référence à cela dans: stackoverflow.com/questions/888414/…
Danny
84
$ git checkout stash @ {0} - <nomfichier> est très utile, merci
gravitation
43
Notez que l' git checkoutapproche copie le fichier exact de la cachette - elle ne le fusionne pas avec ce qui se trouve dans votre répertoire de travail comme le git stash applyferait. (Donc, si vous avez des modifications par rapport à la base sur laquelle la réserve a été créée, elles seront perdues).
peterflynn
4
Notez que pour git stash applyfusionner les modifications dans un fichier qui a été modifié dans l'arborescence de travail depuis que le fichier a été stocké, ce fichier dans l'arborescence de travail doit être transféré. Pour que la fusion automatique fonctionne, les mêmes fichiers ne peuvent pas être modifiés à la fois dans la copie de travail et dans la copie stockée à fusionner. Enfin, l'application de cachette ne supprime pas l'élément de la cachette comme le git stash popferait.
Ville
46

Si vous utilisez git stash applyplutôt que git stash pop, il appliquera la cachette à votre arbre de travail mais gardera toujours la cachette.

Cela fait, vous pouvez add/ commitle fichier que vous souhaitez, puis réinitialisez les modifications restantes.

Tim Henigan
la source
2
Pour apparaître une planque spécifique: git stash pop stash@{0}(liste des changements planqué: git stash list)
MrYoshiji
Si d'autres fichiers dans la cachette provoquent un conflit de fusion, git ne me permet pas de valider le fichier que je veux :(
cambunctious
33

Il existe un moyen facile d'obtenir des modifications à partir de n'importe quelle branche, y compris les stashes:

$ git checkout --patch stash@{0} path/to/file

Vous pouvez omettre la spécification de fichier si vous souhaitez corriger en plusieurs parties. Ou omettez le correctif (mais pas le chemin d'accès) pour obtenir toutes les modifications dans un seul fichier. Remplacez-le 0par le numéro de réserve de git stash list, si vous en avez plusieurs. Notez que cela ressemble diff, et propose d'appliquer toutes les différences entre les branches. Pour obtenir des modifications à partir d'un seul commit / stash, jetez un œil à git cherry-pick --no-commit.

Walf
la source
Est-ce que cela copie le fichier exact de la cachette ou fusionne-t-il? En cas de copie, si vous avez des modifications depuis la création du stash, elles seront perdues.
Danijel
2
@Danijel Read git help checkout. --patchfait une fusion interactive, il applique tout morceau que vous approuvez dans le shell (ou tout ce que vous enregistrez si vous choisissez de dire ele patch). Le chemin seul écrasera le fichier, comme je l'ai écrit, "toutes les modifications".
Walf
1
légère amélioration: git config --global alias.applydiffat '!git checkout --patch "$1" -- $(git diff --name-only "$1"^ "$1")' - alors faire git applydiffat stash@{4}n'utilise que des fichiers qui ont changé entre la cachette et son parent.
Mark
25

Réponse courte

Pour voir l'intégralité du fichier: git show stash@{0}:<filename>

Pour voir le diff: git diff stash@{0}^1 stash@{0} -- <filename>

Luboš Turek
la source
1
Je ne pense pas qu'il était concerné par l'affichage de la cachette pour un fichier particulier - seulement en faisant apparaître ce fichier.
Amalgovinus
1
C'est ce que je cherchais. Ensuite, vous pouvez remplacer diffpar difftoolpour utiliser votre diff externe préféré.
Peet Brits
19
$ git checkout stash@{0} -- <filename>

Remarques:

  1. Assurez-vous de mettre de l'espace après le "-" et le paramètre de nom de fichier

  2. Remplacez zéro (0) par votre numéro de réserve spécifique. Pour obtenir la liste de dissimulation, utilisez:

    git stash list
    

D' après la réponse de Jakub Narębski - Version plus courte

RAM
la source
11

Vous pouvez obtenir le diff pour une cachette avec " git show stash@{0}" (ou quel que soit le numéro de la cachette; voir "git stash list"). Il est facile d'extraire la section du diff pour un seul fichier.

Nathan Kitchen
la source
2
Plus simple encore pour les esprits comme moi: utilisez git show stashpour afficher la cachette la plus haute (normalement la seule que vous ayez). De même, vous pouvez afficher la différence entre votre branche actuelle et la cachette avec git diff head stash.
Dizzley
5

Le concept le plus simple à comprendre, même s'il n'est peut-être pas le meilleur, est que vous avez modifié trois fichiers et que vous souhaitez en cacher un.

Si vous faites git stashpour les ranger tous, git stash applypour les ramener à nouveau puis git checkout f.csur le fichier en question pour le réinitialiser efficacement.

Lorsque vous souhaitez décompresser ce fichier, exécutez-le git reset --hard, puis exécutez-le à git stash applynouveau, en profitant du fait que git stash applyla différence de la pile de stash n'est pas effacée.

Philluminati
la source
1

Si les fichiers cachés doivent fusionner avec la version actuelle, utilisez les méthodes précédentes en utilisant diff. Sinon, vous pouvez utiliser git poppour les décompresser, git add fileWantToKeeppour organiser votre fichier, et faire un git stash save --keep-index, pour tout ranger sauf ce qui est sur scène. N'oubliez pas que la différence de cette façon avec les précédentes est qu'elle "fait sortir" le fichier de la cachette. Les réponses précédentes le conservent git checkout stash@{0} -- <filename>pour qu'il se déroule selon vos besoins.

Hola Soy Edu Feliz Navidad
la source
0

Utilisez ce qui suit pour appliquer les modifications à un fichier dans une cachette à votre arborescence de travail.

git diff stash^! -- <filename> | git apply

C'est généralement mieux que d'utiliser git checkoutcar vous ne perdrez pas les modifications que vous avez apportées au fichier depuis que vous avez créé la cachette.

cambunctious
la source