Matheus Santana

Le contexte est tout quand il s'agit de lire, comprendre et faire évoluer une base de code – qui peut avoir été autour depuis longtemps et modifiée par des dizaines de développeurs. Dans de tels cas, le débogage de nos applications peut impliquer plus que simplement effacer le courant mauvais code. Des défauts peuvent avoir été introduits assez longtemps auparavant et de nombreuses questions peuvent se poser quand vient le temps de les corriger enfin:

  • Qui a introduit le bug?
  • Pourquoi est-ce arrivé?
  • Le problème est-il le résultat d'une mise à jour de version unique ou est-il le résultat cumulatif d'un tas de modifications mineures?

Répondre à ces questions peut aider beaucoup à comprendre le contexte dans lequel une modification qui n'est plus correcte a été introduite, puis à appliquer les bonnes solutions. C'est là que git bisect est particulièrement pratique.

Couper en deux effectue une recherche binaire – l'un des algorithmes de recherche les plus emblématiques de l'informatique – sur notre arbre git afin de trouver un commit spécifique qui a introduit un changement recherché. Le vocabulaire semble fantaisiste, mais cela signifie essentiellement:

  1. Choisissez un commit qui divise l'arbre en deux parties – dites un commit UNE
  2. Est-ce un engagement, UNE, affecté par le changement que vous recherchez?
  3. Si oui, recommencez à partir de 1 en utilisant le sous-arbre composé des commits précédant UNE
  4. Sinon, recommencez à partir de 1 en utilisant le sous-arbre composé par les commits suivants UNE
  5. Arrêtez-vous si vous atteignez un seul commit.

En fin de compte, cela nous donne généralement (et j'espère) un dernier commit final dans lequel le changement que nous recherchons a été initialement appliqué.

La fonction de recherche binaire (diviser l'arbre en deux parties encore et encore) nous permet d'obtenir une réponse plus rapidement. Imaginez que nous recherchons un mot dans le dictionnaire. Une façon pratique dont nous pouvons aborder cette tâche est de l'ouvrir à une page sur le milieu de la masse et de répondre ensuite: notre parole est-elle sur cette page? Sinon, nous pouvons recommencer en ouvrant la partie la plus à gauche de ce qui reste – si le mot que nous recherchons précède lexicographiquement les mots sur la page sur laquelle nous sommes – ou sinon la partie la plus à gauche du lot jusqu'à ce que nous répondions joyeusement «oui ! »À notre question directrice.

Cette question est essentielle pour bissecter. Nous devons avoir une façon objective de répondre à git: est-ce le compromis que je recherche? Pour ce faire, la méthode la plus utilisée consiste à effectuer des tests automatisés. Voyons comment cela fonctionne dans la pratique…

(Nous utiliserons ruby ​​et rspec pour cet exemple, mais la technique est indépendante du langage et peut être utilisée avec n'importe quel dépôt git)

Au nom de la science, introduisons un défaut dans la grande base de code de la bibliothèque Faraday. Ensuite, nous prétendrons que nous ne le savons pas et que nous voulons découvrir dans quel commit le bogue a été introduit.

Installer

J'ai injecté dans l'arbre git original de Faraday un engagement défectueux à rechercher et à détruire – dans ce post, nous nous en tenons à la partie recherche, cependant. Il nous suffit donc de cloner le référentiel:

$ git clone https://github.com/embs/faraday-bisect

et commencer à bissecter avec

$ git bisect start

La prochaine chose que nous devons fournir à bissect est l'état du commit actuel: est-ce bogué ou non? Dans notre exemple, répondre à cette question signifie exécuter cette spécification et vérifier les résultats:

$ bundle exec rspec spec/faraday/request/authorization_spec.rb

Un tas d'échecs de tests, donc c'est un mauvais commit. Nous en informons le processus de bissecte en tapant:

$ git bisect bad

Nous devons également fournir à bisect une bonne version – c'est-à-dire un bon commit – à utiliser comme limite supérieure de notre recherche. Eh bien, il existe plusieurs façons de le trouver, mais pour cet exemple, nous allons avec celui-ci:

qui est le commit introduisant le fichier RSpec que nous utilisons comme question directrice – et nous devons savoir que tous les tests étaient verts à ce stade. Nous devons donc courir

$ git bisect good f1b2657523f9d95ba5594edce7f707a2545171f8

Binaire à la recherche de l'arbre git

Git nous saute ensuite sur le prochain commit qu'il «pense» divise notre arbre en deux morceaux et affiche à quelle distance nous sommes de trouver notre coupable:

Bissection: 105 révisions restent à tester après cela (environ 7 étapes)
(a3689ecfe6f1734863de778192124df914c44052) Corrige le style Rubocop / les méthodes SingleLine.

Nous courrons authorization_spec.rb encore:

$ bundle exec rspec spec/faraday/request/authorization_spec.rb

et marquer l'engagement comme bon avec $ git bisect good si les tests réussissent ou échouent avec $ git bisect bad si les tests échouent.

À la fin de la journée, nous pourrions trouver quelqu'un à blâmer:

85ba6ccb9f000a47666d1302406f11fbc4b88aaf est le premier mauvais commit
commit 85ba6ccb9f000a47666d1302406f11fbc4b88aaf
Auteur: Matheus Santana
Date: mer.12 juin 15:06:33 2019-0300

PUNAISE

: 040000 040000 c4910aca60cd5d0f91e300c9cfdcdc617a43246e cb3023f40a74a96dee5da25bacea66f0a2600ab2 M lib

Consultez cet asciinéma pour l'exemple complet:

Automatisation de la recherche avec git bisect run

Dans le cas où nous avons un script gérant un code de sortie approprié (0 pour le succès, autre valeur sinon), nous pouvons automatiser le processus itératif et laisser git le faire pour nous avec

$ git bisect run our_script_or_command_here

Cela nécessite toujours de tout configurer avant la bissection. Donc, pour notre exemple, nous devrions $ git bisect start, $ git bisect bad et $ git bisect good f1b2657523f9d95ba5594edce7f707a2545171f8 tout comme nous l'avons fait auparavant dans notre phase de configuration.

Cependant, la commande que nous fournissons à git ne peut pas être juste

$ bundle exec rspec spec/faraday/request/authorization_spec.rb

parce que Faraday utilise simplecov pour vérifier la couverture du code et la sortie pour exécuter un seul fichier de spécification renvoie généralement autre chose que 0. Mais nous pouvons automatiser notre recherche avec un script d'aide comme celui-ci et l'enregistrer sous bisect-faraday.sh:

Après cela, nous courons

$ git bisect run ./bisect-faraday.sh

Nous y voilà! L'exemple d'automatisation complet peut être vu ci-dessous:

Si vous souhaitez approfondir les profondeurs de cet outil particulier, le meilleur endroit serait probablement les documents officiels: