Git - Trouver le commit introduisant une régression

dim. 08 juil. 2018 by Marmotte

Git est un système de gestion de versions décentralisé. Le côté décentralisé implique que chaque client télécharge une copie complète du dépôt, la plupart des opérations étant donc réalisées directement sur la machine de l'utilisateur.

Cet article fait partie d'une série expliquant quelques fonctionnalités avancées que j'utilise régulièrement.

  1. Généralités et notations
  2. Quelques configurations
  3. Notes diverses
  4. Dépôts et répertoires de travail
  5. L'historique local
  6. Retravailler l'historique des commits
  7. Trouver le commit introduisant une régression
  8. Récupérer des commits perdus

Lorsqu'on rencontre un bug, il est courant de retourner à un point précédent dans l'historique des commits, pour vérifier s'il était déjà présent ou pas. Dans le cas d'une régression, il est possible d'automatiser partiellement ou totalement la recherche du commit l'ayant introduite avec la commande git bisect.

Mise à jour 12/10/2019 : Ajout du paragraphe manquant sur le retour à l'état normal.

Initialisation

Après avoir trouvé un commit auquel le bug est présent (souvent le dernier commit de la branche), et un autre auquel le bug est absent, on peut lancer le processus de bisection :

$ git bisect start
$ git bisect good <good-commit>
$ git bisect bad <bad-commit>

Si un ou plusieurs répertoires ou fichiers sont soupçonnés de contenir le bug, il est possible de préciser leurs chemins lors du lancement du processus. Cela permettra de réduire le nombre de commits testés, puisque git ne passera alors que sur les commits modifiant ces éléments.

$ git bisect start -- <some-file> <some-directory>

Git change ensuite automatiquement de commit courant entre les deux.

Exécution manuelle

Après avoir testé manuellement la validité du commit courant (en exécutant des tests unitaires, en vérifiant le contenu d'un fichier, ou d'une autre manière), il faut indiquer à git si le commit courant est valide ou non. Cela se fait simplement avec les commandes git bisect good (bug absent) ou git bisect bad (bug présent). Il est aussi possible d'utiliser la commande git bisect skip pour indiquer qu'il est impossible de vérifier la validité de ce commit (par exemple, si un autre bug empêche de vérifier la présence de celui que l'on recherche).

Le commit courant est modifié automatiquement, pour se déplacer sur le prochain à tester. On recommence alors la vérification de validité du commit courant en boucle, jusqu'à la fin du processus.

Suivre l'avancement

Il est possible de suivre l'avancement du processus au moyen de la commande git bisect log. La liste des commits testés, ainsi que sa description et le résultat donné pour chacun est alors affiché. Cela peut permettre de vérifier ce qui a été fait en cas de soupçon d'une fausse manipulation.

Exécution automatique

Lorsque c'est possible, il est intéressant d'écrire un script qui vérifie la validité du commit de manière automatique. Ce script devra renvoyer le code de retour 0 (bug absent), 1 (bug présent) ou 125 (vérification impossible). On lance enfin le processus automatique avec la commande git bisect run </path/script>.

Résultat final

Une fois la recherche terminée, git peut afficher différents résultats.

La recherche a réussi

Le hash du commit fautif est annoncé :

<commit-hash> is the first bad commit

Les résultats retournés sont incohérents

Un même commit a été annoncé comme valide et invalide.

Ce résultat provient souvent d'une erreur de manipulation, ou d'un bug dans le script de test automatique :

<commit-hash> was both good and bad

Il existe des commits valides plus récents que des commits invalides.

Ce résultat peut provenir d'une erreur dans la validation effectuée, ou d'une précédente correction du même bug. Dans ce second cas, relancer le processus de bisection en partant d'un commit valide plus récent peut permettre de résoudre le problème :

Some good revs are not ancestor of the bad rev.
git bisect cannot work properly in this case.
Maybe you mistook good and bad revs?

Terminer le processus de bisection

De nombreuses références sont ajoutées par git lors de la bisection, pour conserver les résultats des différents tests. Pour terminer, ou annuler, le processus, il est nécessaire de demander à git de tout nettoyer avec la commande git bisect reset.

Sans paramètre, cette commande remet le dépôt dans l'état où il était avant de lancer la bisection. Mais il est aussi possible de lui passer un commit en paramètre, pour se placer sur ce commit après le nettoyage. La référence bisect/bad est alors très utile, puisqu'elle pointe directement le commit déterminé comme fautifen cas de succès de la recherche.