Git avance - 04 - Stash, cherry-pick et bisect : les outils de chirurgie

Les commandes git avancees pour sauver ta journee. Stash pour mettre de cote, cherry-pick pour copier un commit, bisect pour trouver un bug.

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


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

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.