web-dev-qa-db-fra.com

Comment utiliser Git Bisect?

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

Quelqu'un pourrait-il s'il vous plaît démontrer avec un exemple de code:

  1. Comment l'utiliser?
  2. Est-ce juste comme svn blame?
402
IAdapter

L'idée sous-jacente à git bisect est d'effectuer une recherche binaire 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 à la révision current et qu'il fonctionnait à la révision 0. La régression a donc 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 faisant une recherche binaire. C'est ce que fait la commande git bisect. A chaque étape, il essaie 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 extraira un commit. Dans notre cas, ce sera commit 3. Vous devez construire votre programme et vérifier si la régression est présente ou non. Vous devez é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 ait été introduite dans commit 4. Alors la régression n'est pas présente dans cette révision, et on la dit à git.

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

Il passera ensuite à la caisse pour un autre commit. 4 ou 5 (car il n'y a que deux commits). Supposons qu'il ait pris 5. Après une construction, nous testons le programme et voyons que la régression est présente. Nous disons alors à 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 puisque c'est celui qui a introduit la régression, on dit à git:

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

Dans cette situation simple, nous n'avions qu'à tester 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, nous devrions nous attendre à tester 1 + log2 N commits avec git bisect au lieu d'environ 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 cette opération effectuée, vous utilisez git bisect reset pour rétablir l'état d'origine avant d'utiliser la commande git bisect.

588
Sylvain Defresne

git bisect run bissect automatique

Si vous avez un script automatisé ./test dont le statut de sortie est 0 si le test est bon, vous pouvez rechercher automatiquement le bogue avec 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 avec git, il ne disparaît pas lors de la validation précédente lors de la bissection.

J'ai trouvé que très souvent, vous pouvez vous en tirer simplement en copiant le script in-tree en dehors de tree, et éventuellement en jouant avec des variables de type PATH-, et en l'exécutant à partir de là.

Bien sûr, si l'infrastructure de test sur laquelle test repose sur des validations plus anciennes, il n'y a pas de solution et vous devrez procéder manuellement, en décidant comment tester les validations une par une.

Plus de conseils

Restez sur le premier commit qui échoue après la division, au lieu de retourner à 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_GOOD_COMMIT

Voir ce qui a été testé jusqu'à présent (par le manuel good et bad ou run):

git bisect log

Exemple de sortie:

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

Affichez les bonnes et les mauvaises références sur le journal git pour avoir une meilleure idée du temps:

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

Cela montre seulement les commits avec un ref correspondant, ce qui réduit considérablement le bruit, mais inclut les ref générés automatiquement du type:

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

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

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

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

Quelquefois:

  • l'échec se produit rapidement, par exemple une des premières pauses tests
  • le succès prend un certain temps, par exemple le test cassé réussit et tous les autres tests dont nous ne nous soucions pas de suivre

Pour ces cas, par exemple en supposant que l'échec se produise toujours au bout de 5 secondes, et si nous sommes paresseux pour rendre le test plus spécifique que nous le devrions vraiment, 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 quitte 124 alors que l'échec de test-command se termine 1.

Statuts de sortie magique

git bisect run est un peu difficile en ce qui concerne les statuts de sortie:

  • tout ce qui dépasse 127 fait échouer la bissection avec quelque chose comme:

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

    En particulier, un C assert(0) conduit à un SIGABRT et quitte avec le statut 134, très gênant.

  • 125 est magique et permet de sauter la course avec git bisect skip.

    L'intention de ceci est d'aider à ignorer les constructions cassées pour des raisons indépendantes.

Voir man git-bisect pour les détails.

Donc, vous voudrez peut-être 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 <goodcommit>

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

Répéter:

Le problème existe toujours?

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

Résultat:

<abcdef> is the first bad commit

Lorsque vous avez terminé:

git bisect reset
112
Geoffrey Hale

Juste pour ajouter un autre point:

Nous pouvons spécifier un nom de fichier ou un chemin d'accès à git bisect start si nous savons que le bogue provient de fichiers particuliers. Par exemple, supposons que nous sachions que les modifications qui ont provoqué la régression se trouvent dans le répertoire com/workingDir, puis nous pouvons exécuter git bisect start com/workingDir. Cela signifie que seules les validations qui ont modifié le contenu de ce répertoire seront vérifiées. même plus vite.

De plus, s’il est difficile de dire si un commit particulier est bon ou mauvais, vous pouvez lancer git bisect skip, qui l’ignorera. Etant donné qu'il y a suffisamment d'autres commits, git bisect en utilisera un autre pour affiner la recherche.

37
Nicks

$ git bisect .. basiquement a Outil Git pour le débogage . 'Git Bisect' débogue en passant par le précédent commits depuis votre dernier commit actif (connu). Il utilise la recherche binaire pour passer par tous ces commits, pour arriver à celui qui a introduit la régression/le bogue.

$ git bisect start # Bisect de départ

$ git bisect bad # indiquant que le commit en cours (v1.5) a le point "mauvais" de régression /

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

Cette mention de "mauvais" et de "bons" points aidera git bisect (recherche binaire) à sélectionner l'élément central (commit v1.3). Si la régression existe lors de la validation v1.3, vous la définissez comme le nouveau point "incorrect", à savoir ( Good -> v1.0 et Bad -> v1.3 )

$ git bisect bad

ou de la même manière si la v1.3 de commit est sans bogue, vous le définissez comme le nouveau "bon point", c'est-à-dire (* Good -> v1.3 et Bad -> v1.6).

$ git bisect good
11
Nabeel Ahmed

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

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

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

Avec documentation ajoutant:

Parfois, vous ne recherchez 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.
Ou vous recherchez peut-être le premier commit dans lequel les noms de fichiers de code source ont finalement tous été convertis au standard de nommage de votre entreprise. Ou peu importe.

Dans de tels cas, il peut être très déroutant d'utiliser les termes "bon" et "mauvais" pour désigner "l'état avant le changement" et "l'état après le changement".

Vous pouvez donc utiliser les termes "old" et "new", respectivement, à la place de "good" et "bad".
(Notez cependant que vous ne pouvez pas mélanger "good" et "bad" avec "old" et "new" dans une même session.)

Dans cette utilisation plus générale, vous fournissez git bisect avec un commit "new" ayant une propriété et un commit "old" qui ne possède pas cette propriété.

Chaque fois que git bisect extrait un commit, vous vérifiez si ce commit a la propriété:
Si tel est le cas, marquez le commit comme "new"; sinon, marquez-le comme "old".

Lorsque la bissection est terminée, git bisect indiquera quel commit a introduit la propriété.


Voir commit 06e6a74 , commit 21b55e , commit fe67687 (29 juin 2015) par Matthieu Moy (moy) .
Voir commit 21e5cfd (29 juin 2015) par Antoine Delaite (CanardChouChinois) .
(Fusion de Junio ​​C Hamano - gitster - dans commit 22dd6eb , 5 octobre 2015)

1
VonC