Il n’est pas rare de se rendre compte en développement qu’un bug a fait son apparition, mais ne semble pas lié aux travaux en cours. Il a été introduit lors d’un commit précédent et n’a jusqu’alors pas été détecté. Plutôt que de devoir tester chaque commit pour identifier la source de ce bug, Git offre une suite d’outils pour faciliter sa recherche : git bisect
.
Cet outil utilise la recherche dichotomique consistant à séparer la zone de recherche en deux à chaque étape. Nous pouvons ainsi identifier le commit responsable en un nombre limité d’itérations.
Nous commençons par examiner le log à l’aide de la commande git log
pour identifier un « bon » commit, dans lequel nous savons que le bug n’est pas présent.
Nous notons son SHA (les 6 premiers caractères suffisent) puis nous nous plaçons sur ce commit :
git checkout <SHA du bon commit >
Nous nous assurons en lançant la suite de tests ou en vérifiant manuellement que le bug ne se manifeste pas, puis nous passons à l’étape suivante.
Nous continuons l’examen du log pour identifier un « mauvais » commit, dans lequel nous savons que le bug se manifeste. Comme à l’étape précédente, nous vérifions sa présence en nous plaçant sur ce commit et lançant les tests ou manuellement :
git checkout <SHA du mauvais commit>
Une fois les deux commits identifiés, nous pouvons initialiser la recherche :
git bisect start
Puis nous indiquons le « bon » et le « mauvais » commit :
git bisect good <SHA du bon commit>
git bisect bad <SHA du mauvais commit>
Git se place alors sur le milieu entre ces deux SHA, en indiquant combien d’étapes restent.
Bisecting: 28 revisions left to test after this (roughly 5 steps)
Nous testons la présence ou non du bug, et indiquons à git si ce commit est bon ou pas :
git bisect good
# ou
git bisect bad
Il se replace alors entre le dernier bon commit connu et le dernier mauvais connu. Nous n’avons plus qu’à répéter l’opération en testant à chaque commit, jusqu’à trouver le responsable :
<SHA du coupable> is the first bad commit
Il ne reste plus qu’à corriger le bug.
Comme git nous laisse dans l’état detached HEAD
à l’endroit du commit responsable, nous créons une branche pour y résoudre notre bug. Cela a un double avantage :
- nous pouvons y revenir facilement plus tard,
- une fois le bug résolu, nous pourrons voir exactement d’où venait le bug et le commit correspondant à sa résolution.
Nous créons donc notre branche :
git checkout -b fix-annoying-bug
Puis nous corrigeons le bug, avant de commit et de merge dans la branche principale :
# fix bug ...
git add .
git commit -m "Annoying bug fixed"
git checkout master
git merge --no-ff fix-annoying-bug
L’option --no-ff
permet de forcer la création d’un commit de merge, et ainsi de bien montrer le parent du commit de correction. Ainsi plus tard nous saurons exactement quel mauvais commit a nécessité le correctif.
Enfin, nous réinitialisons la recherche pour une utilisation future de git bisect
:
git bisect reset
Grâce à git bisect
, il est très simple de trouver rapidement le commit responsable d’un bug. Ce fonctionnement est d’autant plus efficace si l’on dispose d’une suite de tests pour identifier rapidement un bug.
git bisect
dispose de plusieurs autres options très pratiques, voici les principales :
git bisect run
permet de lancer automatiquement un script à chaque itération, et de marquer le commit comme bon ou mauvais en fonction du résultat du script. Cela permet d’automatiser entièrement le processus.git bisect visualize
permet de visualiser les commits restants dans l’interface graphiquegitk
git bisect skip
permet d’ignorer un ou plusieurs commits dans le cas où nous savons qu’ils ne fonctionnement pas dans l’environnement en cours, pour une raison sans rapport avec le bug recherché
La documentation de git bisect
détaille toutes ces options.