Débusquer une régression avec git bisect

Git bisect n'est pas une commande très connue. Pourtant, elle est extrêmement puissante et quand on en a besoin, on est content de la trouver.

Git-bisect permet de trouver dichotomiquement la source d'une régression dans un historique donné. Ça claque, non ? Si vous n'avez rien compris, pas d'inquiétudes, nous allons détailler tout cela.

Pour que vous compreniez bien de quoi il s'agit, laissez moi illustrer mon propos au travers d'un cas concret. Imaginons qu'un bug sournois surgisse soudainement et vienne perturber la bonne marche de votre projet. Après quelques investigations dignes des plus fameux scientifiques , vous découvrez que le vil dysfonctionnement n'est apparu que récemment. Il s'agit bien là d'une régression !

Certes, vous pourriez perdre du temps à corriger cette régression de la manière classique, en plongeant dans le code poussiéreux et plein de toiles d'araignées virtuelles, mais il serait bien plus simple de découvrir exactement à quelle moment elle est apparue, quel est le commit correspondant, pour avoir une idée trés précise de sa cause.

C'est exacement ce que permet de faire git-bisect, d'une manière particulièrement intuitive. Indiquez lui un commit présentant le dysfonctionnement (HEAD), et un commit passé, n'importe lequel, qui ne la présente pas.

Git vous place alors dans votre historique de développement, pile au milieu entre les deux commit. Après vérification, vous indiquez à git si la régression apparait ou pas. Et on recommence. Chaque fois, on divise par 2 l'intervalle des commits ayant potentiellement introduit le bug, jusqu'à débusquer le fautif. Génial, non ?

Git bisect en pratique

La première étape, comme d'habitude, c'est de s'assurer qu'on va travailler sur un dépot propre

git stash

Attention, vous devez vous placer a la racine du depot git, sous peine de voir apparaitre ce message d'erreur :

You need to run this command from the toplevel of the working tree

Et c'est parti. On initialise la bisection, en indiquant à git un mauvais et un bon commit :

git bisect start
bit bisect bad # le commit actuel
git bisect good 
>>> Bisecting: 96 revisions left to test after this

96 commits ont potentiellement introduit la régression. On indique si le commit actuel est correct ou pas :

git bisect good # ou git bisect bad
Bisecting : 48 revisions left to test after this

Et on continue, encore et encore, jusqu'à ce qu'on se trouve LE vil commit à l'origine de ce temps perdu (mais ça aurait pu être pire).

d109d47732cb85652b79d679edd7bfe2379e5707 is first bad commit
...

A tout moment, vous pouvez obtenir un historique de votre parcours :

git bisect log

Si a un moment précis, vous souhaitez ne pas tester un commit en particulier, pour n'importe quelle raison, utilisez la commande

git bisect skip

Tout ceci est bel et bon, mais il y a mieux (non ?! et si !). Supposions que vous soyez un fan de TDD. Vous disposez surement d'un script qui teste automatiquement si le code actuel est bon ou pas (en fait, si c'était le cas, la régression n'aurait pu apparaitre, mais c'est une autre histoire). Il est alors possible d'automatiser la recherche :

git bisect start HEAD  <mauvais commit>  # raccourci
git bisect run script

Allez prendre un café, git travaille pour vous. Note : le script doit renvoyer 0 si le code est correct, 1 s'il est incorrect, et 125 s'il est intestable (git skip).

Pendant la bisection, git créé une branche spéciale dédiée. N'oubliez pas de repasser sur votre branche de développement quand vous aurez débusqué la régression. Il est possible de le faire simplement en tapant :

git bisect reset

Voilà, avec tout ça, fini les fastidieuses recherches de régression.