Boire ou coder ... Pourquoi choisir?
Publié le 05 avril 2010 22:00

Corrigez vos régressions avec git bisect

On a beau écrire autant de tests que l'on voudra, il arrivera quasiment inévitablement que l'on ait des régressions dont on ne rends pas forcément compte tout de suite mais parfois après quelques commit supplémentaires, voir plusieurs jours plus tard. Et il est parfois assez complexe de comprendre quelle "amélioration" a inséré cette régression.

Si vous avez versionné votre projet avec GIT cependant, vous allez pouvoir utiliser git bisect afin de traquer ce bug et le corriger plus aisément.

Travaux pratiques

Prenons moqueur avec gitg afin de visualiser le log des commit. Notre arbre de commit ressemble à ceci :

Cependant, nous nous rendons compte que nous avons un bug. Nous réussissons à le reproduire et savons donc à peu près d'où il vient. Cependant nous ne savons pas ou il a été introduit. Faisons donc un bisect afin de trouver cela !

Initialisons le bisect :

git bisect start

Puis nous savons que le commit sur lequel nous sommes actuellement n'est pas bon. Nous le déclarons donc comme en erreur :

git bisect bad

Nous avons maintenant un arbre de commit qui ressemble à ceci :

Il nous faut ensuite trouver un endroit ou notre code sera correct. Allons sur le tag 0.0.1.

git checkout 0.0.1

Vu que nous arrivons à reproduire notre erreur, nous voyons qu'elle ne se produit pas à ce niveau. Nous pouvons donc déclarer notre code comme correct.

git bisect good

Nous avons maintenant un arbre de commit qui ressemble à cela :

Nous pouvons voir que GIT a, automatiquement, ajouté des références aux deux commit que nous avons vérifié comme étant correct ou non. Et, si vous regardez votre console, vous verrez la chose suivante :

((no branch)) damien@pcdamien ~/projects/moqueur $ git bisect good
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[72d09e65ca2fee3d281200d92ec63f15e8c19257] when the url is a regex, we get it's parameters in the content

GIT, voyant que nous avons un commit correct et un incorrect, nous a déplacé automatiquement au commit situé exactement entre les deux. Ainsi, nous n'avons plus qu'à vérifier si notre bug se produit ou non. Quelle chance nous avons, il est correct !

((no branch)) damien@pcdamien ~/projects/moqueur $ git bisect good
Bisecting: 1 revisions left to test after this (roughly 1 steps)
[74d0368b8a7327c6ef7500589b6c8359d55ec8c0] specify if the url has been mocked or not in the counter

Et si nous regardons notre arbre de commit, il ressemble maintenant à ceci :

GIT nous a, de nouveau, créé une référence indiquant que le commit que nous venons de tester est correct. Et il nous a envoyé automatiquement sur le commit situé exactement au milieu. Testons ceci à nouveau, jusqu'à ce que nous tombions sur le commit exact qui implémente la régression que nous cherchons à corriger.

((no branch)) damien@pcdamien ~/projects/moqueur $ git bisect good
74d0368b8a7327c6ef7500589b6c8359d55ec8c0 is the first bad commit
commit 74d0368b8a7327c6ef7500589b6c8359d55ec8c0
Date:   Fri Mar 26 11:52:53 2010 +0100</p>

<p>    specify if the url has been mocked or not in the counter</p>

<p>:100644 100644 0e2cf9e02a4595c1a04da670eed109a128fc014f dbfc82c51599afe166b317fb4a5b24c7dbc50451 M	moqueur.js
:040000 040000 412cdf8ce8d2f25cfb5474bee65597d3dc0febf0 e115c9172ed71841466cc009adec71bb50e8ea7f M	spec

Et notre arbre de commit ressemble à ceci :

Nous savons ou est notre bug. Nous pouvons le corriger !

Et nous pouvons arrêter le bisect (et donc enlever toutes les références good/bad créées).

git bisect reset

N'oubliez pas d'en profiter pour écrire un test qui vous évitera d'avoir cette régression une seconde fois dans le futur.

Dans la théorie

La recherche dichotomique

Tous ceux qui ont fait un tout petit peu d'algorithmie (pour ma part, je me souviens avoir fait ça en première année de BTS, aux alentours de novembre. C'est donc réellement les bases), vous vous souvenez probablement des diverses méthodes de recherche, notamment la recherche dichotomique.

Explication rapide : dans une liste triée de chiffres allant de 1 à 10, nous désirons trouver ou se situe le 3. Nous pourrions parcourir la liste dans l'ordre et aller jusqu'à trouver notre chiffre. Mais si nous cherchions 9, nous mettrions énormément de temps à le trouver.

En recherche dichotomique, nous allons faire la chose suivante : prendre le chiffre 5 qui est au milieu. Si celui-ci est plus petit que le chiffre que nous cherchons, nous prendrons ensuite le chiffre situé entre 1 et 5 (2 ou 3). Et ce, jusqu'à trouver le chiffre approprié. Nous réduisons fortement le nombre d'itérations nécessaires pour trouver les chiffres éloignés de notre point de départ ou d'arrivée.

Application au bisect

C'est exactement ce que nous faisons ici. Vous ignorez quand a été implémenté le bug que vous cherchez à corriger. Vous pourriez donc chercher dans chacun de vos commit manuellement. Mais cela vous prendrait énormément de temps si le commit est ancien.

Nous créons donc une intervalle réduite, basée sur le dernier commit, que l'on sait incorrect. Et un autre commit, que l'on sait correct. Puis on fait une recherche dichotomique en divisant à chaque fois par 2 l'intervalle entre un commit correct et un commit incorrect.

En 4 étapes, nous avons donc réussi à localiser un commit qui pouvait se cacher n'importe ou dans les 8 commit entre 0.0.1 et 0.0.2.

C'est lorsque vous utilisez bisect que vous apprécierez tout particulièrement d'avoir fait de petits commit et pas de commit énormes (ce que j'appelle BFUC)

Et encore un point pour git et les gestionnaires de version en général ! ;)

Commentaires

Lemée Mathilde
Lemée Mathilde dit: 06 avril 2010 22:00 Site web

Très bon article ! Curieuse de savoir la signification de BFUC ? :)

Damien
Damien dit: 06 avril 2010 22:00 Site web

Il y a un <acronym> avec le BFUC. Ca signifie "Big Fat Ugly Commit" ;)

Jean-Marc Fontaine
Jean-Marc Fontaine dit: 06 avril 2010 22:00 Site web

Merci pour cet article Damien !

shingara
shingara dit: 15 avril 2010 22:00 Site web

C'est marrant, j'étais persuader qu'on pouvait laisser git faire le sous sans nous. Car la technique est un peu fastidieuse si notre arbre est grand.

Damien
Damien dit: 15 avril 2010 22:00 Site web

Git en fait déjà pas mal sans nous. Il serait cependant un peu difficile de lui en faire faire plus tout en restant générique. La reproduction d'une erreur n'est pas forcément dans les tests (le test qui la reproduit n'a pas forcément été implémenté dès le début).

La technique n'est pas si fastidieuse que cela. C'est cependant assez chiant sur un arbre grand en effet car cela implique de répéter la même tâche (répétition du bug) plusieurs fois de suite.

catwell
catwell dit: 24 septembre 2010 10:10 Site web

Le sujet est certes un peu vieux mais pour info ce que dit shingara existe, ça s'appelle git bisect run. Il faut fournir un test unitaire sous forme de script pour l'utiliser.

Postez un commentaire

Markdown activé