web-dev-qa-db-fra.com

Ne cachez qu'un fichier parmi plusieurs fichiers modifiés par Git?

Comment puis-je cacher un seul des multiples fichiers modifiés sur ma branche?

2638
Rachel

Attention

Comme indiqué dans les commentaires, cela met tout dans la réserve, mise en scène et non mise en scène. Le paramètre --keep-index laisse l'index seul après la fin du stockage. Cela peut entraîner des conflits de fusion lorsque vous supprimez ultérieurement la cachette.


Cela cachera tout ce que vous n'avez pas encore ajouté. Il suffit de git add les choses que vous voulez conserver, puis lancez-le.

git stash --keep-index

Par exemple, si vous souhaitez fractionner un ancien commit en plusieurs ensembles de modifications, vous pouvez utiliser cette procédure:

  1. git rebase -i <last good commit>
  2. Marquez certaines modifications avec edit.
  3. git reset HEAD^
  4. git add <files you want to keep in this change>
  5. git stash --keep-index
  6. Réparez les choses au besoin. N'oubliez pas de modifier git add.
  7. git commit
  8. git stash pop
  9. Répétez, à partir de # 5, si nécessaire.
  10. git rebase --continue
1260
bukzor

Vous pouvez également utiliser git stash save -p "my commit message". De cette façon, vous pouvez sélectionner les morceaux à ajouter à la réserve, ainsi que des fichiers entiers. 

Vous serez invité à quelques actions pour chaque morceau:

   y - stash this hunk
   n - do not stash this hunk
   q - quit; do not stash this hunk or any of the remaining ones
   a - stash this hunk and all later hunks in the file
   d - do not stash this hunk or any of the later hunks in the file
   g - select a hunk to go to
   / - search for a hunk matching the given regex
   j - leave this hunk undecided, see next undecided hunk
   J - leave this hunk undecided, see next hunk
   k - leave this hunk undecided, see previous undecided hunk
   K - leave this hunk undecided, see previous hunk
   s - split the current hunk into smaller hunks
   e - manually edit the current hunk
   ? - print help
2868
konrad.kruczynski

Puisque git consiste fondamentalement à gérer un référentiel entier contenu et un index (et non un ou plusieurs fichiers), git stash traite avec tout le répertoire de travail.

En fait, depuis Git 2.13 (T2 2017), vous pouvez cacher des fichiers individuels avec git stash Push :

git stash Push [--] [<pathspec>...]

Lorsque pathspec est attribué à 'git stash Push', le nouveau stash enregistre les états modifiés uniquement pour les fichiers correspondant à pathspec.

Voir " Stash modifications apportées à des fichiers spécifiques " pour plus d'informations.

Le scénario de test s'explique de lui-même:

test_expect_success 'stash with multiple pathspec arguments' '
    >foo &&
    >bar &&
    >extra &&
    git add foo bar extra &&

    git stash Push -- foo bar &&   

    test_path_is_missing bar &&
    test_path_is_missing foo &&
    test_path_is_file extra &&

    git stash pop &&
    test_path_is_file foo &&
    test_path_is_file bar &&
    test_path_is_file extra

La réponse initiale (ci-dessous, juin 2010) concernait la sélection manuelle de ce que vous voulez cacher. 

Casebash commentaires:

Ceci (la solution originale stash --patch) est agréable, mais souvent j’ai modifié beaucoup de fichiers, l’utilisation de patch est donc agaçante.

bukzor 's answer (voté en novembre 2011) suggère une solution plus pratique, basée sur
git add + git stash --keep-index.
Allez voir et invitez sa réponse, qui devrait être la réponse officielle (au lieu de la mienne).

À propos de cette option, chhh indique un flux de travaux alternatif dans les commentaires:

vous devriez "git reset --soft" après une telle réserve pour récupérer votre mise en scène claire:
Afin de revenir à l'état d'origine - qui est une zone de stockage claire et avec seulement quelques modifications sélectionnées non stockées, vous pouvez réinitialiser doucement l'index pour obtenir (sans rien commettre comme vous - bukzor - l'a fait).


(Réponse originale juin 2010: cachette manuelle)

Cependant, git stash save --patch pourrait vous permettre d’obtenir la sauvegarde partielle que vous recherchez:

Avec --patch, vous pouvez sélectionner interactivement des morceaux dans le diff entre HEAD et l’arbre de travail à stocker.
L'entrée de stash est construite de manière à ce que son état d'index soit identique à celui de votre référentiel et que son arbre de travail ne contienne que les modifications que vous avez sélectionnées de manière interactive. Les modifications sélectionnées sont ensuite annulées à partir de votre arbre de travail.

Cependant, cela sauvegardera l’index complet (ce qui peut ne pas être ce que vous voulez, car il peut inclure d’autres fichiers déjà indexés), ainsi qu’une arborescence partielle (qui pourrait ressembler à celle que vous voulez cacher).

git stash --patch --no-keep-index

pourrait être un meilleur ajustement.


Si --patch ne fonctionne pas, un processus manuel peut:

Pour un ou plusieurs fichiers, une solution intermédiaire consisterait à:

  • copiez-les en dehors du dépôt Git
    (En fait, eleotlecram propose une alternative intéressante )
  • git stash
  • les recopier
  • git stash # cette fois, seuls les fichiers que vous voulez sont cachés
  • git stash pop stash@{1} # réappliquez toutes vos modifications de fichiers
  • git checkout -- afile # réinitialiser le fichier au contenu HEAD, avant toute modification locale

À la fin de ce processus plutôt fastidieux, vous ne conserverez qu'un ou plusieurs fichiers.

324
VonC

Lorsque git stash -p (ou git add -p avec stash --keep-index) était trop lourd, j'ai trouvé qu'il était plus facile d'utiliser diff, checkout et apply:

Pour "cacher" un fichier/répertoire particulier uniquement:

git diff path/to/dir > stashed.diff
git checkout path/to/dir

Ensuite

git apply stashed.diff
80
blueyed

Utilisez git stash Push, comme ceci:

git stash Push [--] [<pathspec>...]

Par exemple:

git stash Push -- my/file.sh

Ceci est disponible depuis Git 2.13, publié au printemps 2017.

58
sandstrom

Disons que vous avez 3 fichiers 

a.rb
b.rb
c.rb

et vous voulez cacher seulement b.rb et c.rb mais pas a.rb

tu peux faire quelque chose comme ça

# commit the files temporarily you don't want to stash
git add a.rb
git commit -m "temp" 

# then stash the other files
git stash save "stash message"

# then undo the previous temp commit
git reset --soft HEAD^
git reset

Et vous avez terminé! HTH.

45
venkatareddy

Une autre façon de faire ceci:

# Save everything
git stash 

# Re-apply everything, but keep the stash
git stash apply

git checkout <"files you don't want in your stash">

# Save only the things you wanted saved
git stash

# Re-apply the original state and drop it from your stash
git stash apply stash@{1}
git stash drop stash@{1}

git checkout <"files you put in your stash">

Je me suis présenté après avoir (encore une fois) visité cette page et n'avais pas aimé les deux premières réponses (la première réponse ne répond simplement pas à la question et je n'aimais pas trop travailler avec le mode interactif -p).

L'idée est la même que celle suggérée par @VonC en utilisant des fichiers hors du référentiel: vous enregistrez les modifications souhaitées quelque part, vous supprimez les modifications non souhaitées dans votre réserve, puis vous appliquez à nouveau les modifications que vous avez déplacées. Cependant, j'ai utilisé la réserve git comme "quelque part" (et par conséquent, il y a une étape supplémentaire à la fin: supprimer les changements que vous avez mis dans la réserve, car vous les avez également déplacés).

27
Jasper

Mise à jour (14/02/2015) - J'ai un peu réécrit le script pour mieux gérer les conflits, qui devraient maintenant être présentés comme des conflits non fondus plutôt que des fichiers .rej.


Je trouve souvent plus intuitif de faire l'inverse de l'approche de @ bukzor. C’est-à-dire mettre en scène certains changements, puis ne stocker que les changements mis en scène. 

Malheureusement, git n'offre pas de cachette git --only-index ou similaire, j'ai donc préparé un script pour le faire.

#!/bin/sh

# first, go to the root of the git repo
cd `git rev-parse --show-toplevel`

# create a commit with only the stuff in staging
INDEXTREE=`git write-tree`
INDEXCOMMIT=`echo "" | git commit-tree $INDEXTREE -p HEAD`

# create a child commit with the changes in the working tree
git add -A
WORKINGTREE=`git write-tree`
WORKINGCOMMIT=`echo "" | git commit-tree $WORKINGTREE -p $INDEXCOMMIT`

# get back to a clean state with no changes, staged or otherwise
git reset -q --hard

# Cherry-pick the index changes back to the index, and stash.
# This cherry-pick is guaranteed to succeed
git cherry-pick -n $INDEXCOMMIT
git stash

# Now cherry-pick the working tree changes. This cherry-pick may fail
# due to conflicts
git cherry-pick -n $WORKINGCOMMIT

CONFLICTS=`git ls-files -u`
if test -z "$CONFLICTS"; then
    # If there are no conflicts, it's safe to reset, so that
    # any previously unstaged changes remain unstaged
    #
    # However, if there are conflicts, then we don't want to reset the files
    # and lose the merge/conflict info.
    git reset -q
fi

Vous pouvez enregistrer le script ci-dessus en tant que git-stash-index quelque part sur votre chemin, puis l'invoquer en tant que git stash-index

# <hack hack hack>
git add <files that you want to stash>
git stash-index

À présent, la réserve contient une nouvelle entrée contenant uniquement les modifications que vous avez effectuées et votre arborescence de travail contient toujours les modifications non mises en scène.

Dans certains cas, les modifications de l'arborescence de travail peuvent dépendre des modifications d'index. Par conséquent, lorsque vous stockez les modifications d'index, les modifications de l'arborescence de travail entrent en conflit. Dans ce cas, vous obtiendrez les conflits habituels que vous pouvez résoudre avec git merge/git mergetool/etc.

23
JesusFreke

La création de branches dans Git étant simple, vous pouvez simplement créer une branche temporaire et y archiver les fichiers individuels.

19
shangxiao

Juste au cas où vous voudriez dire réellement annulez les modifications chaque fois que vous utilisez git stash (et n’utilisez pas vraiment git stash pour le stocker temporairement), dans ce cas, vous pouvez 

git checkout -- <file>

[REMARQUE

Ce git stash est une alternative simple et rapide aux branches et aux tâches.

11
Devesh

Enregistrez le code suivant dans un fichier, par exemple, nommé stash. L'utilisation est stash <filename_regex>. L'argument est l'expression régulière du chemin complet du fichier. Par exemple, pour cacher a/b/c.txt, stash a/b/c.txt ou stash .*/c.txt, etc.

$ chmod +x stash
$ stash .*.xml
$ stash xyz.xml

Code à copier dans le fichier:

#! /usr/bin/expect --
log_user 0
set filename_regexp [lindex $argv 0]

spawn git stash -p

for {} 1 {} {
  expect {
    -re "diff --git a/($filename_regexp) " {
      set filename $expect_out(1,string)
    }
    "diff --git a/" {
      set filename ""
    }
    "Stash this hunk " {
      if {$filename == ""} {
        send "n\n"
      } else {
        send "a\n"
        send_user "$filename\n"
      }
    }
    "Stash deletion " {
      send "n\n"
    }
    eof {
      exit
    }
  }
}
10
apricot

Le problème de la solution «intermédiaire» de VonC consistant à copier des fichiers hors du référentiel Git est que vous perdez les informations de chemin, ce qui rend la copie de fichiers très difficile ultérieurement.

Il est plus facile de trouver tar (des outils similaires suffiront probablement) au lieu de copier:

  • tar cvf /tmp/stash.tar chemin/vers/un/chemin du fichier/vers/un/autre/fichier (... etc.)
  • chemin de sortie git/vers/une/chemin du fichier/vers/une/autre/fichier
  • git stash
  • tar xvf /tmp/stash.tar
  • etc. (voir la suggestion «intermédiaire» de VonC)
7
eleotlecram

Cela peut être fait facilement en 3 étapes en utilisant SourceTree.

  1. Commencez temporairement tout ce que vous ne voulez pas cacher.
  2. Git ajoute tout le reste, puis range-le.
  3. Déclenchez votre validation temporaire en exécutant git reset, en la ciblant avant votre validation temporaire.

Tout cela peut être fait en quelques secondes dans SourceTree, où vous pouvez simplement cliquer sur les fichiers (ou même les lignes individuelles) que vous souhaitez ajouter. Une fois ajoutés, il suffit de les engager dans un commit temporaire. Ensuite, cochez la case pour ajouter toutes les modifications, puis cliquez sur stocker pour tout cacher. Une fois les modifications cachées, jetez un coup d'œil sur votre liste de validation et notez le hachage de la validation avant votre validation temporaire, puis exécutez 'git reset hash_b4_temp_commit', ce qui revient fondamentalement à «réactiver» la validation en réinitialisant votre branche. commettre juste avant. Maintenant, il ne vous reste plus que ce que vous ne voulez pas cacher.

7
Triynko

Je voudrais utiliser git stash save --patch. Je ne trouve pas que l’interactivité soit gênante car il contient des options pour appliquer l’opération souhaitée à des fichiers entiers.

6

Parfois, j'ai apporté une modification non liée sur ma branche avant de l'avoir validée, et je souhaite la transférer dans une autre branche et la valider séparément (comme un maître). Je fais ça:

git stash
git checkout master
git stash pop
git add <files that you want to commit>
git commit -m 'Minor feature'
git stash
git checkout topic1
git stash pop
...<resume work>...

Notez que la première stash & stash pop peut être éliminée, vous pouvez reporter toutes vos modifications sur la branche master lorsque vous passez à la caisse, mais uniquement en l'absence de conflit. De même, si vous créez une nouvelle branche pour les modifications partielles, vous aurez besoin de la réserve.

Vous pouvez le simplifier sans aucun conflit ni nouvelle branche:

git checkout master
git add <files that you want to commit>
git commit -m 'Minor feature'
git checkout topic1
...<resume work>...

Cachette n'est même pas nécessaire ...

6
void.pointer

Vous pouvez simplement faire ceci:

git stash Push "filename"

ou avec un message optionnel

git stash Push -m "Some message" "filename"
6
vinodsaluja

Chaque réponse ici est tellement compliquée ...

Qu'en est-il de cela "stash":

git diff /dir/to/file/file_to_stash > /tmp/stash.patch
git checkout -- /dir/to/file/file_to_stash

Ceci pour faire apparaître le fichier revenir en arrière:

git apply /tmp/stash.patch

Exactement le même comportement que de stocker un fichier et de le restaurer.

6
Christophe Fondacci

J'ai passé en revue les réponses et les commentaires pour ceci et un certain nombre de sujets similaires. Sachez qu'aucune des commandes suivantes n'est correcte pour pouvoir stocker aucun fichier spécifique suivi/non suivi:

  • git stash -p (--patch): sélectionnez manuellement les mecs, en excluant les fichiers non suivis
  • git stash -k (--keep-index): cache tous les fichiers suivis/non suivis et les conserve dans le répertoire de travail
  • git stash -u (--include-untracked): cache tous les fichiers suivis/non suivis
  • git stash -p (--patch) -u (--include-untracked): commande invalide

Actuellement, la méthode la plus raisonnable pour pouvoir stocker des fichiers spécifiques suivis/non suivis est la suivante:

  • Valider temporairement les fichiers que vous ne voulez pas cacher
  • Ajouter et cacher
  • Pop le commit temporaire

J'ai écrit un script simple pour cette procédure dans une réponse à une autre question , et il y a des étapes pour effectuer la procédure dans SourceTree ici .

3
ZimbiX

Solution

Changements locaux: 

  • fichier_A (modifié) non mis en scène 
  • fichier_B (modifié) non mis en scène 
  • fichier_C (modifié) non mis en scène 

Pour créer une réserve "my_stash" avec uniquement les modifications de fichier_C :

1. git add file_C
2. git stash save --keep-index temp_stash
3. git stash save my_stash
4. git stash pop stash@#{1}

Terminé.


Explication

  1. ajoutez file_C à la zone de transfert
  2. crée une réserve temporaire nommée "temp_stash" et conserve les modifications dans fichier_C
  3. créer le stash voulu ("my_stash") avec seulement les modifications sur file_C
  4. appliquez les modifications de "temp_stash" (fichier_A et fichier_B) sur votre code local et supprimez la stash

Vous pouvez utiliser git status entre les étapes pour voir ce qui se passe.

3
Alex 75

Si vous ne souhaitez pas spécifier de message avec vos modifications stockées, transmettez le nom du fichier après un double tiret.

$ git stash -- filename.ext

S'il s'agit d'un fichier non suivi/nouveau, vous devez d'abord le mettre en scène.

Cette méthode fonctionne dans les versions 2.13+ de git

2
sealocal
git add .                           //stage all the files
git reset <pathToFileWillBeStashed> //unstage file which will be stashed
git stash                           //stash the file(s)
git reset .                         // unstage all staged files
git stash pop                       // unstash file(s)
2
novice

Pour cacher un seul fichier, utilisez git stash --patch [file].

Cela va à l'invite: Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]? ?. Il suffit de taper a (cachez ce morceau et tous les morceaux suivants dans le fichier) et tout va bien.

2
vnbrs

Dans cette situation, je git add -p (interactif), git commit -m blah et je cache ce qui reste, si nécessaire.

2
J0hnG4lt

Situation similaire. J'ai commis et réalisé que ce n'était pas correct.

git commit -a -m "message"
git log -p

Sur la base des réponses cela m'a aidé.

# revert to previous state, keeping the files changed
git reset HEAD~
#make sure it's ok
git diff
git status
#revert the file we don't want to be within the commit
git checkout specs/nagios/nagios.spec
#make sure it's ok
git status
git diff
#now go ahead with commit
git commit -a -m "same|new message"
#eventually Push tu remote
git Push
1
David Hrbáč

Lorsque vous essayez de basculer entre deux branches, cette situation se produit.

Essayez d'ajouter les fichiers en utilisant "git add filepath".

Plus tard exécuter cette ligne 

git stash --keep-index

1
Sireesh Yarlagadda

Je ne sais pas comment le faire en ligne de commande, en utilisant uniquement SourceTree. Disons que vous avez modifié le fichier A et que vous avez deux éléments de modification dans le fichier B. Si vous souhaitez ne stocker que le deuxième élément dans le fichier B et ne pas modifier le reste, procédez comme suit:

  1. Tout mettre en scène
  2. Apportez des modifications à votre copie de travail qui annulent toutes les modifications du fichier A. (par exemple, lancez un outil de diff externe et faites correspondre les fichiers.)
  3. Faire en sorte que le fichier B ait l’impression que seule la deuxième modification lui soit appliquée. (par exemple, lancer un outil de diff externe et annuler le premier changement.)
  4. Créez une réserve en utilisant "Conserver les modifications planifiées".
  5. Décomposer tout
  6. Terminé!
1
Juozas Kontvainis

Je n'ai trouvé aucune réponse pour ce dont j'avais besoin et c'est aussi simple que

git add -A
git reset HEAD fileThatYouWantToStash
git commit -m "committing all but one file"
git stash

Cela cache exactement un fichier.

0
Sebastian

Une façon compliquée serait de tout d'abord tout commettre:

git add -u
git commit // creates commit with sha-1 A

Réinitialisez le commit original mais récupérez le fichier_un_du nouveau commit:

git reset --hard HEAD^
git checkout A path/to/the_one_file

Maintenant, vous pouvez cacher le fichier_un_fichier:

git stash

Nettoyez en enregistrant le contenu validé dans votre système de fichiers tout en réinitialisant le commit d'origine:

git reset --hard A
git reset --soft HEAD^

Oui, un peu maladroit ...

0
Martin G

Réponse rapide


Pour rétablir un fichier modifié spécifique dans git, vous pouvez effectuer la ligne suivante:

git checkout <branch-name> -- <file-path>

Voici un exemple concret:

git checkout master -- battery_monitoring/msg_passing.py

0
Benyamin Jafari