Comment utiliser git bisect?

J’ai lu des articles disant que git bisect est génial, mais je ne suis pas natif et je ne comprends pas pourquoi c’est génial.

Pourriez-vous s’il vous plaît montrer sur un échantillon de code ce qui est si incroyable à ce sujet? Est-ce juste comme svn blame ?

    L’idée derrière git bisect est d’effectuer une recherche binary dans l’historique pour trouver une régression particulière. Imaginez que vous ayez l’historique de développement suivant:

     ... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current 

    Vous savez que votre programme ne fonctionne pas correctement lors de la révision en current et qu’il fonctionnait à la révision 0 . Donc, la régression a probablement été introduite dans l’un des commits 1 , 2 , 3 , 4 , 5 , current .

    Vous pouvez essayer de vérifier chaque commit, le construire, vérifier si la régression est présente ou non. S’il y a un grand nombre de commits, cela peut prendre beaucoup de temps. Ceci est une recherche linéaire. Nous pouvons faire mieux en effectuant une recherche binary. C’est ce que fait la commande git bisect . À chaque étape, il tente de réduire de moitié le nombre de révisions potentiellement mauvaises.

    Vous utiliserez la commande comme ceci:

     $ git stash save $ git bisect start $ git bisect bad $ git bisect good 0 Bisecting: 2 revisions left to test after this (roughly 2 steps) [< ... sha ... >] 3 

    Après cette commande, git va extraire un commit. Dans notre cas, ce sera la validation 3 . Vous devez créer votre programme et vérifier si la régression est présente ou non. Vous devrez également indiquer à git le statut de cette révision avec soit git bisect bad si la régression est présente, soit git bisect good si ce n’est pas le cas.

    Supposons que la régression a été introduite dans commit 4 . Ensuite, la régression n’est pas présente dans cette révision et on lui dit de git .

     $ make $ make test ... ... ... $ git bisect good Bisecting: 0 revisions left to test after this (roughly 1 step) [< ... sha ... >] 5 

    Il vérifiera alors un autre commit. Soit 4 ou 5 (car il n’y a que deux commits). Supposons qu’il ait choisi 5 . Après une construction, nous testons le programme et voyons que la régression est présente. On lui dit alors de git :

     $ make $ make test ... ... ... $ git bisect bad Bisecting: 0 revisions left to test after this (roughly 0 steps) [< ... sha ... >] 4 

    Nous testons la dernière révision, 4 . Et comme c’est lui qui a introduit la régression, on lui dit:

     $ make $ make test ... ... ... $ git bisect bad < ... sha ... > is the first bad commit < ... commit message ... > 

    Dans cette situation simple, nous n’avons dû tester que 3 versions ( 3 , 4 , 5 ) au lieu de 4 ( 1 , 2 , 3 , 4 ). C’est une petite victoire, mais c’est parce que notre histoire est si petite. Si la plage de recherche est de N commits, on devrait s’attendre à tester 1 + log2 N commits avec git bisect au lieu de N / 2 commits avec une recherche linéaire.

    Une fois que vous avez trouvé le commit qui a introduit la régression, vous pouvez l’étudier pour trouver le problème. Une fois cela fait, vous utilisez git bisect reset pour tout remettre à l’état d’origine avant d’utiliser la commande git bisect .

    git bisect run la bissect automatique

    Si vous avez un script automatisé ./test qui a le statut de sortie 0, si le test est OK, vous pouvez automatiquement trouver le bogue avec l’ bisect run :

     git checkout KNOWN_BAD_COMMIT git bisect start # Confirm that our test script is correct, and fails on the bad commit. ./test # Should output != 0. echo $? # Tell Git that the current commit is bad. git bisect bad # Same for a known good commit in the past. git checkout KNOWN_GOOD_COMMIT ./test # Should output 0. echo $? # After this, git automatically checks out to the commit # in the middle of KNOWN_BAD_COMMIT and KNOWN_GOOD_COMMIT. git bisect good # Bisect automatically all the way to the first bad or last good rev. git bisect run ./test # End the bisect operation and checkout to master again. git bisect reset 

    Cela suppose bien sûr que si le script de test ./test est suivi, il ne disparaîtra pas lors d’une validation antérieure lors de la bissection.

    J’ai constaté que très souvent, vous pouviez vous contenter de copier le script in-tree à partir de l’arborescence, et éventuellement de jouer avec des variables similaires à PATH , et de l’exécuter à partir de là.

    Bien entendu, si l’infrastructure de test sur laquelle dépend le test casse sur les anciens commits, alors il n’y a pas de solution, et vous devrez faire les choses manuellement, en décidant comment tester les validations les unes après les autres.

    Plus de conseils

    Restez sur le premier commit qui échoue après la bissect au lieu de revenir au master :

     git bisect reset HEAD 

    start + initiale bad et good en une fois:

     git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~ 

    est le même que:

     git checkout KNOWN_BAD_COMMIT git bisect start git bisect bad git bisect good KNOWN_BAD_COMMIT 

    Voir ce qui a été testé jusqu’ici (par manuel good et bad ou run ):

     git bisect log 

    Sortie de l’échantillon:

     git bisect log git bisect start # bad: [00b9fcdbe7e7d2579f212b51342f4d605e53253d] 9 git bisect bad 00b9fcdbe7e7d2579f212b51342f4d605e53253d # good: [db7ec3d602db2d994fe981c0da55b7b85ca62566] 0 git bisect good db7ec3d602db2d994fe981c0da55b7b85ca62566 # good: [2461cd8ce8d3d1367ddb036c8f715c7b896397a5] 4 git bisect good 2461cd8ce8d3d1367ddb036c8f715c7b896397a5 # good: [8fbab5a3b44fd469a2da3830dac5c4c1358a87a0] 6 git bisect good 8fbab5a3b44fd469a2da3830dac5c4c1358a87a0 # bad: [dd2c05e71c246f9bcbd2fbe81deabf826c54be23] 8 git bisect bad dd2c05e71c246f9bcbd2fbe81deabf826c54be23 # bad: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05] 7 git bisect bad c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05 # first bad commit: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c0 

    Afficher les bonnes et les mauvaises références sur git log pour avoir une meilleure idée du temps:

     git log --decorate --pretty=fuller --simplify-by-decoration master 

    Cela montre uniquement les commits avec une ref correspondante, ce qui réduit considérablement le bruit, mais inclut les références générées automatiquement:

     refs/bisect/good* refs/bisect/bad* 

    qui nous dit quels commits nous avons marqués comme bons ou mauvais.

    Considérez ce test repo si vous souhaitez jouer avec la commande.

    L’échec est rapide, le succès est lent

    Parfois:

    • l’échec se produit rapidement, par exemple, une des premières ruptures de test
    • le succès prend un certain temps, par exemple les tests de réussite cassés, et tous les autres tests qui ne nous intéressent pas suivent

    Pour ces cas, par exemple en supposant que l’échec se produit toujours avec 5 secondes, et si nous sums paresseux pour rendre le test plus précis, nous pouvons utiliser timeout comme dans:

     #!/usr/bin/env bash timeout 5 test-command if [ $? -eq 1 ]; then exit 1 fi 

    Cela fonctionne car timeout sort 124 alors que l’échec de test-command termine 1 .

    Statuts de sortie magiques

    git bisect run est un peu difficile sur les états de sortie:

    • quelque chose au-dessus de 127 fait que la bissection échoue avec quelque chose comme:

       git bisect run failed: exit code 134 from '../test -aa' is < 0 or >= 128 

      En particulier, une SIGABRT C assert(0) conduit à un SIGABRT et sort avec le statut 134, très ennuyeux.

    • 125 est magique et fait sauter le run avec le git bisect skip

    Voir l’ man git-bisect pour les détails.

    Donc, vous pourriez vouloir utiliser quelque chose comme:

     #!/usr/bin/env bash set -eu ./build status=0 ./actual-test-command || status=$? if [ "$status" -eq 125 ] || [ "$status" -gt 127 ]; then status=1 fi exit "$status" 

    Testé sur git 2.16.1.

    TL; DR

    Début:

     $ git bisect start $ git bisect bad $ git bisect good  

    Bisecting: X revisions left to test after this (roughly Y steps)

    Répéter:

    Problème existe toujours?

    • Oui: $ git bisect bad
    • Non: $ git bisect good

    Résultat:

      is the first bad commit 

    Lorsque vous avez terminé:

     git bisect reset 

    Juste pour append un autre point:

    Nous pouvons spécifier un nom de fichier ou un chemin d’access à git bisect start si nous soaps que le bogue provient de fichiers particuliers. Par exemple, supposons que nous sachions que les modifications à l’origine de la régression se trouvaient dans le répertoire com / workingDir, alors nous pouvons lancer git bisect start com/workingDir Cela signifie que seuls les commits ayant modifié le contenu de ce répertoire seront vérifiés. les choses encore plus vite.

    De plus, s’il est difficile de savoir si un commit particulier est bon ou mauvais, vous pouvez lancer git bisect skip , qui l’ignorera. Étant donné qu’il y a suffisamment d’autres commits, git bisect utilisera un autre pour restreindre la recherche à la place.

    $ git bisect .. bisect $ git bisect .. basiquement un outil Git pour le débogage . ‘Git Bisect’ débogue en parcourant les commits précédents depuis votre dernier commit de travail (connu). Il utilise la recherche binary pour parcourir tous ces commits, pour accéder à celui qui introduit la régression / bogue.

    $ git bisect start # Commencer la bissect

    $ git bisect bad # indiquant que le commit en cours (v1.5) a le sharepoint régression / réglage “bad”

    $ git bisect good v1.0 # en mentionnant le dernier bon commit de travail (sans régression)

    Cette mention de «mauvais» et de «bons» points aidera git bisect (recherche binary) à choisir l’élément central (commit v1.3). Si la régression est présente lors de la validation v1.3, vous la définissez comme le nouveau “mauvais” point, à savoir ( bon -> v1.0 et mauvais -> v1.3 )

     $ git bisect bad 

    ou de la même façon, si le commit v1.3 ne contient pas de bogue, vous le définirez comme le nouveau ‘Good point’ (* Good -> v1.3 et Bad -> v1.6).

     $ git bisect good 

    Remarque: les termes good et bad ne sont pas les seuls que vous pouvez utiliser pour marquer un commit avec ou sans une certaine propriété.

    Git 2.7 (Q4 2015) a introduit de nouvelles options de git bisect .

      git bisect start [--term-{old,good}= --term-{new,bad}=] [--no-checkout] [ [...]] [--] [...] 

    Avec documentation en ajoutant:

    Parfois, vous ne cherchez pas le commit qui a introduit une rupture, mais plutôt un commit qui a provoqué un changement entre un autre “ancien” état et un “nouvel” état .

    Par exemple, vous pouvez rechercher le commit qui a introduit un correctif particulier.
    Vous pouvez également rechercher la première validation dans laquelle les noms de fichiers du code source ont finalement tous été convertis selon la norme de dénomination de votre société. Ou peu importe.

    Dans de tels cas, il peut être très déroutant d’utiliser les termes “bien” et “mauvais” pour désigner “l’État avant le changement” et “l’État après le changement”.

    Au lieu de cela, vous pouvez utiliser les termes ” old ” et ” new “, respectivement, à la place de ” good ” et ” bad “.
    (Mais notez que vous ne pouvez pas mélanger ” good ” et ” bad ” avec ” old ” et ” new ” en une seule session.)

    Dans cet usage plus général, vous fournissez à git bisect un ” new ” commit ayant une propriété et un ” old ” commit qui ne possède pas cette propriété.

    Chaque fois que git bisect extrait une validation, vous testez si cette validation a la propriété:
    Si c’est le cas, marquez le commit comme ” new “; sinon, marquez-le comme ” old “.

    Lorsque la bissection est faite, git bisect signale quel commit a introduit la propriété.


    Voir commit 06e6a74 , commit 21b55e3 , commit fe67687 (29 juin 2015) par Matthieu Moy ( moy ) .
    Voir commit 21e5cfd (29 juin 2015) par Antoine Delaite ( CanardChouChinois ) .
    (Fusionné par Junio ​​C Hamano – gitster – dans commit 22dd6eb , 05 oct. 2015)