git stash : un seul fichier, sans perdre le reste

Stashez un seul fichier pendant que le reste de votre travail en cours reste en place : git stash push -- <chemin>, le flux interactif -p, et les trois façons de récupérer vos changements.

HUMANpar @mrsaynothing35d5 min de lecture

Une revue de code tombe pendant que trois fichiers sont à moitié modifiés et qu'un seul peut bouger. Tout stasher et retaper les deux autres est le rituel dont personne ne regrette la disparition.

TL;DR : git stash push -m "pourquoi" -- <chemin> stashe exactement les chemins nommés et laisse le reste de votre arborescence sale tranquille. Récupérez les changements avec git stash pop (ou sortez un seul fichier de n'importe quel stash avec git restore --source stash@{0} -- <chemin>). La forme pathspec est arrivée avec git 2.13, sorti en mai 2017 — toute chaîne d'outils des huit dernières années l'a.

git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop

Stashez le fichier, pas l'arbre.

Comment stasher un seul fichier dans git ?

Un git stash simple balaie toute l'arborescence — chaque modification suivie part dans le stash et tout revient à HEAD. C'est la mauvaise forme quand l'arbre sale est mixte : un fichier est prêt à partir chez quelqu'un, le reste est franchement inachevé. La solution, c'est le verbe push avec un pathspec, selon la documentation git-stash :

# avant : trois fichiers sales
$ git status --short
 M app.conf
 M notes.md
 M main.py

# stasher uniquement app.conf
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak

$ git status --short
 M notes.md
 M main.py

app.conf revient à son état commité et se retrouve dans stash@{0} ; notes.md et main.py n'ont pas bougé. Le message -m est optionnel mais vaut les trois secondes qu'il coûte dès que git stash list affiche plus d'une entrée — les stash baptisés « WIP » vieillissent mal.

Deux détails qui comptent :

  • Le pathspec vient après --. Tout ce qui suit le double tiret est un chemin, pas un flag. C'est la même convention que git checkout -- <chemin> et elle n'est pas décorative : git stash push -- main.py et git stash push main.py diffèrent sur les versions plus anciennes, où un chemin nu pouvait être mal interprété.
  • Les fichiers non suivis demandent -u. Un fichier tout neuf n'a pas d'historique suivi à stasher, donc un push simple l'ignore. git stash push -u -- newfile.py l'inclut ; -a va plus loin et balaie aussi les fichiers ignorés.

Comment stasher une partie d'un fichier ?

Quand le fichier lui-même est à moitié prêt — trois bons hunk, un honteux — le flux interactif le découpe :

git stash -p            # ou : git stash push -p

Git parcourt chaque hunk et demande Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. Répondez y pour les hunks qui vont dans le stash, n pour ceux qui restent. Résultat : un stash ne contenant que ce que vous avez approuvé, le reste du fichier restant sale dans l'arborescence. La même mécanique hunk par hunk alimente git add -p, donc le réflexe se transfère.

git stash push -- <chemin> git stash -p
Granularité fichiers entiers hunks individuels
Vitesse une commande, scriptable interactif, hunk par hunk
Répétable en CI oui (chemins en arguments) non — exige un humain au prompt
Idéal pour « ce fichier, pas le reste » « ce changement, pas celui-là »
Depuis git 2.13 (mai 2017) disponible depuis longtemps

Règle simple tirée du modèle même des docs : pathspec quand la frontière est le fichier, -p quand elle est à l'intérieur.

Comment poper un seul fichier depuis le stash ?

git stash pop restaure stash@{0} et supprime l'entrée. C'est le chemin normal — mais pop est tout-ou-rien par stash, et un conflit interrompt le pop en préservant l'entrée. Deux options plus fines :

# 1. appliquer sans supprimer (relançable sans risque)
git stash apply stash@{0}

# 2. sortir UN fichier d'un stash, entrée intacte
git restore --source stash@{0} -- app.conf
# orthographe d'avant 2.23 de la même opération :
git checkout stash@{0} -- app.conf

La forme restore/checkout répond au cas « j'ai stashé trois changements ensemble et il ne m'en faut plus qu'un » — elle copie le contenu stashé de ce chemin dans l'arborescence et laisse stash@{0} en place. Notez qu'elle écrase la copie présente dans l'arborescence ; si les modifications en cours sur ce chemin comptent, diffusez d'abord :

git diff stash@{0} -- app.conf

Les stash sont stockés comme de vrais commits sur une pile façon reflog, raison pour laquelle stash@{0} accepte toute syntaxe commit-ish — et pourquoi un stash supprimé par erreur reste récupérable via le reflog jusqu'au prochain garbage collection.

Quand le stash est-il le mauvais outil ?

Un stash est un brouillon, pas une branche : aucun nom dans git branch, aucune revue, aucun diff affiché par défaut, et des entrées qui s'empilent en silence jusqu'à l'oubli. Si le travail en cours doit survivre à des changements de contexte entre machines ou sur plusieurs jours, committez-le sur une branche — un historique rattaché vaut mieux qu'une pile sans nom. Quand ces commits parqués devront atterrir ailleurs, git cherry-pick les transportera. Et quand l'objectif est d'annuler plutôt que de garer, l'arbre de décision d'annuler le dernier commit s'applique.

Tous les modes d'échec ci-dessous sortent du comportement documenté — rien d'inventé :

Symptôme Cause Correctif
Fichier absent après le stash chemins non suivis ignorés git stash push -u -- <chemin>
error: Your local changes ... would be overwritten au pop le chemin a changé depuis le stash committez ou stashez le nouvel état, puis pop
Changements popés disparus le pop a rencontré un conflit et s'est interrompu résolvez, puis git stash apply
« C'était quel stash ? » pile de stash sans nom des messages -m, à chaque fois

La note de version de mai 2017 qui a ajouté le pathspec à git stash push a huit ans, et la plupart des réflexes git stash lui sont antérieurs. Ça mérite un réapprentissage : le balayage global est devenu le cas particulier, pas le défaut.