04 - Stash, cherry-pick et bisect : les outils de chirurgie
Ce que tu vas apprendre
- Stash : mettre du travail de cote sans commiter
- Cherry-pick : copier un commit spécifique d'une branche a une autre
- Bisect : trouver le commit qui a casse quelque chose en O(log n)
Prerequisites
- 00 - Introduction
- Savoir naviguer entre les branches
Stash : la boîte a gants
Scénario classique. Tu es en plein milieu d'une feature, du code compile a moitie, et ton collegue te dit "il y a un bug urgent en prod, tu peux regarder ?". Tu ne veux pas commiter un truc a moitie fait. Tu ne veux pas perdre ton travail non plus.
bashgit stash
Tout ton travail en cours (fichiers modifies et fichiers indexes) est mis de cote. Ton working directory revient a l'état du dernier commit. Tu peux changer de branche, fixer le bug, revenir, et récupérer ton travail :
bash# Tu stash ton travail en cours
git stash
# Tu fixes le bug
git checkout main
git checkout -b fix/urgent-bug
# ... fix ...
git commit -m "fix(auth): handle null user in session"
git push -u origin fix/urgent-bug
# Tu reviens a ta feature
git checkout feat/add-search
git stash pop
git stash pop restaure le dernier stash et le supprime de la pile. Si tu veux le garder au cas ou, utilise git stash apply a la place (il restaure sans supprimer).
Le piège du stash anonyme
Le problème, c'est que git stash sans message créé une entree cryptique :
bashgit stash list
# stash@{0}: WIP on feat/add-search: a1b2c3d feat: add input
# stash@{1}: WIP on main: e4f5g6h chore: update deps
# stash@{2}: WIP on feat/auth: i7j8k9l feat: add login
Au bout de cinq stashs, tu ne sais plus lequel contient quoi. Un de mes juniors avait 12 stashs accumules. Il a fini par tout drop parce qu'il ne savait plus ce qui etait important.
La solution : nomme tes stashs.
bashgit stash push -m "wip: search autocomplete, manque les tests"
bashgit stash list
# stash@{0}: On feat/add-search: wip: search autocomplete, manque les tests
Immediatement comprehensible. Prends l'habitude.
Stash partiel
Tu ne veux pas stash tout. Juste certains fichiers.
bash# Stash seulement des fichiers specifiques
git stash push -m "wip: search component" src/search.ts src/search.test.ts
# Stash interactif : choisis hunk par hunk
git stash push -p
Le -p (patch) te demande pour chaque bloc de modifications si tu veux le stash ou pas. Pratique quand tu as touche a plein de fichiers mais que tu veux garder certaines modifs.
Gerer la pile
bashgit stash list # voir tous les stashs
git stash show stash@{2} # voir le diff d'un stash
git stash apply stash@{2} # appliquer un stash sans le supprimer
git stash drop stash@{2} # supprimer un stash
git stash clear # supprimer tous les stashs (attention)
Mon conseil : ne laisse jamais plus de deux ou trois stashs trainer. Si c'est plus vieux qu'une semaine et que tu ne sais plus ce que c'est, drop-le. Si c'etait important, tu t'en serais souvenu.
Cherry-pick : le copier-coller de commits
Tu as un commit sur une branche et tu en as besoin sur une autre. Pas toute la branche, juste ce commit precis.
Exemple concret : tu as fixe un bug sur feat/add-search et tu veux ce fix sur main sans merger toute la feature (qui n'est pas finie).
bash# Tu trouves le hash du commit
git log feat/add-search --oneline
# a1b2c3d feat: add search input
# e4f5g6h fix(api): handle timeout on search endpoint <-- celui-la
# i7j8k9l feat: add search results page
# Tu le cherry-pick sur main
git checkout main
git cherry-pick e4f5g6h
Git créé un nouveau commit sur main avec le meme contenu que e4f5g6h, mais un hash différent. L'original reste sur sa branche.
Autre cas frequent : un hotfix sur main qui doit aussi aller sur une branche de release.
bashgit checkout release/1.2
git cherry-pick abc1234
Quand ca conflicte
Si le cherry-pick créé des conflits (le contexte du code a change entre les deux branches), git te le dit :
bashgit cherry-pick e4f5g6h
# CONFLICT (content): Merge conflict in src/api.ts
Tu resous les conflits normalement, puis :
bashgit add src/api.ts
git cherry-pick --continue
Ou si tu veux annuler :
bashgit cherry-pick --abort
Un avertissement : n'abuse pas du cherry-pick. Si tu cherry-pick régulièrement entre branches, c'est souvent un signe que ta stratégie de branching a un problème. Le cherry-pick est un scalpel, pas un couteau de cuisine.
Bisect : la recherche binaire de bugs
Bisect est la commande la plus sous-estimee de git. Tu as un bug, tu sais qu'il n'existait pas il y a deux semaines, mais tu ne sais pas quel commit l'a introduit. Avec 200 commits entre les deux, tu ne vas pas les tester un par un.
git bisect fait une recherche binaire. Tu lui donnes un commit "bon" (le bug n'existait pas) et un commit "mauvais" (le bug existe). Il te fait tester le commit du milieu. Tu dis si c'est bon ou mauvais. Il coupe en deux a nouveau. En 7-8 étapes, tu trouves le commit coupable parmi 200.
bashgit bisect start
git bisect bad HEAD # le commit actuel a le bug
git bisect good v1.0 # cette version n'avait pas le bug
# Git checkout un commit au milieu
# Tu testes...
git bisect good # pas de bug ici
# Git checkout un autre commit...
git bisect bad # bug present ici
# ... et ainsi de suite
# Bisecting: 0 revisions left to test
# abc1234 is the first bad commit
7 étapes pour 128 commits. 8 étapes pour 256. C'est du O(log n). Redoutable.
Bisect automatise
Le vrai pouvoir de bisect, c'est l'automatisation. Si tu as un test qui reproduit le bug :
bashgit bisect start
git bisect bad HEAD
git bisect good v1.0
git bisect run bun test src/search.test.ts
Git lance le test sur chaque commit intermediaire. Si le test passe (exit code 0), le commit est marque "good". S'il echoue (exit code non-zero), il est marque "bad". En quelques secondes, tu as le coupable.
bash# Quand tu as fini
git bisect reset
Ca te ramene a HEAD. N'oublie pas cette commande, sinon tu restes en "detached HEAD" sur un commit aleatoire.
La prochaine fois qu'un junior te dit "je comprends pas d'ou vient ce bug, ca marchait la semaine dernière", montre-lui bisect. Ca prend 2 minutes au lieu de 2 heures a lire du code. Sur paltemps.fr, je l'ai utilise trois fois le mois dernier. Trois fois, le commit coupable etait pas celui que je soupconnais.
Article précédent : 03 - Conventional commits Article suivant : 05 - Résoudre les conflits