517 votes

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 n'arrive pas à comprendre pourquoi c'est génial.

Quelqu'un pourrait-il me faire une démonstration avec un exemple de code ?

  1. Comment l'utiliser ?
  2. Est-ce que c'est juste comme svn blame ?

1 votes

@01 : Comme le dit le livre git : effectuer une recherche par force brute dans l'historique du projet .

14 votes

Pas si ///brute :-), il utilise la recherche binaire.

1 votes

"git blame" est similaire à "svn blame". "git bisect" est une chose complètement différente.

731voto

Sylvain Defresne Points 15231

L'idée derrière 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 au niveau de la current révision, et qu'il travaillait à la révision 0 . Ainsi, la régression a probablement été introduite dans l'un des commits 1 , 2 , 3 , 4 , 5 , current .

Vous pourriez 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. Il s'agit d'une recherche linéaire. On peut faire mieux en faisant une recherche binaire. C'est ce que le git bisect fait. À chaque étape, elle essaie de réduire de moitié le nombre de révisions qui sont 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 vérifier 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 devrez également dire à git le statut de cette révision avec soit git bisect bad si la régression est présente, ou git bisect good si ce n'est pas le cas.

Supposons que la régression ait été introduite dans le commit 4 . Alors la régression n'est pas présente dans cette révision, et nous lui disons 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. Nous lui demandons 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, nous lui disons de git :

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

Dans cette situation simple, nous n'avons eu à 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 historique est si petit. Si l'intervalle 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 que c'est fait, vous utilisez git bisect reset pour tout remettre dans son état d'origine avant d'utiliser git bisect commandement.

7 votes

Je vais contredire ici, c'est une bonne explication de bisect mais cela ne m'aide pas vraiment à l'utiliser. Surtout depuis que j'ai réussi à trouver un bon commit et que je suis sur cette branche maintenant. De ce point de vue, cette explication ne m'aide pas du tout. Comment puis-je spécifier la mauvaise branche sans la vérifier, par exemple

6 votes

Vous pouvez utiliser git bisect bad <rev> [<rev>...] pour marquer des révisions spécifiques comme mauvaises (ou bonnes avec git bisect good <rev> [<rev>...] ). rev peut être n'importe quel identifiant de révision comme un nom de branche, un tag, un hash de commit (ou un préfixe unique de hash de commit), ...

48 votes

...et une fois que vous avez terminé, vous tapez git bisect reset pour tout remettre sur le récent commit

191voto

Ciro Santilli Points 3341

git bisect run bissection automatique

Si vous avez un système automatisé ./test script qui a le statut de sortie 0 si le test est OK, vous pouvez automatiquement trouver le bug 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 test script ./test fait l'objet d'un suivi git, afin qu'il ne disparaisse pas lors d'un commit antérieur pendant la bisection.

J'ai constaté que, très souvent, on peut s'en sortir en copiant simplement le script de l'arbre, et en jouant éventuellement avec les éléments suivants PATH -et l'exécuter à partir de là.

Bien sûr, si l'infrastructure de test sur laquelle test dépend de ruptures sur des commits plus anciens, alors il n'y a pas de solution, et vous devrez faire les choses manuellement, en décidant comment tester les commits un par un.

J'ai cependant constaté que l'utilisation de cette automatisation fonctionne souvent, et peut être un énorme gain de temps pour les tests plus lents qui se trouvent dans votre arriéré de tâches, où vous pouvez simplement le laisser fonctionner pendant la nuit, et peut-être avoir votre bug identifié le lendemain matin, cela vaut la peine d'essayer.

Plus de conseils

Rester sur le premier commit défaillant après bisect au lieu de revenir à master :

git bisect reset HEAD

start + initial bad et good en une seule fois :

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

est la même chose 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 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

Montrer les bons et mauvais refs sur git log pour avoir une meilleure notion du temps :

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

Cela ne montre que les commits avec une référence correspondante, ce qui réduit considérablement le bruit, mais inclut les références autogénérées de type :

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

qui nous indiquent les engagements que nous avons marqués comme bons ou mauvais.

Pensez à ce répertoire de test si vous voulez jouer avec la commande.

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

Parfois :

  • l'échec est rapide, par exemple, l'un des premiers tests se brise
  • le succès prend un certain temps, par exemple, le test cassé passe, et tous les autres tests dont nous ne nous soucions pas suivent.

Pour ces cas, par exemple en supposant que l'échec se produit toujours dans les 5 secondes, et si nous sommes paresseux pour rendre le test plus spécifique comme nous le devrions, nous pouvons utiliser timeout comme dans :

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

Cela fonctionne puisque timeout sort de 124 tandis que l'échec de test-command sort de 1 .

Statuts de sortie magiques

git bisect run est un peu pointilleux sur les statuts de sortie :

  • tout ce qui est supérieur à 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 sort avec le statut 134, très ennuyeux.

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

    L'intention est d'aider à éviter les constructions cassées pour des raisons non liées.

Voir man git-bisect pour les détails.

Vous pouvez donc 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.

7 votes

Comment git sait-il empêcher la disparition de votre nouveau test lors d'un retour en arrière ou d'une bisection vers une révision précédente ou mauvaise (qui n'avait pas votre nouveau test écrit) ?

8 votes

@thebjorn vous avez raison : pour autant que je sache, soit le test doit être sur un exécutable externe dans PATH, soit dans un fichier non suivi dans le repo. Dans de nombreux cas, c'est possible : mettez le test dans un fichier séparé, incluez le boilerplate de test nécessaire avec un fichier bien conçu test_script + suite de tests modulaires, et l'exécuter à partir du fichier séparé tout en bissectant. Quand vous avez corrigé, fusionnez le test dans la suite de test principale.

1 votes

@CiroSantilli Désolé, j'ai annulé votre modification ci-dessous, parce que je pense que c'est plus explicatif pour les débutants et que c'est juste pour ajouter un point supplémentaire à votre réponse (pas une réponse exacte comme la vôtre - donc en mentionnant cela, c'est pour ajouter un point).

144voto

Geoffrey Hale Points 5306

TL;DR

Commencez :

$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>

Ou

$ git bisect start
$ git bisect good
$ git bisect bad <badcommit>

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

Répète :

Le problème existe toujours ?

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

Résultat :

<abcdef> is the first bad commit

Quand c'est fait :

git bisect reset

44voto

Nicks Points 238

Juste pour ajouter un point supplémentaire :

Nous pouvons spécifier un nom de fichier ou un chemin d'accès à git bisect start dans le cas où nous savons que le bogue provient de fichiers particuliers. Par exemple, Supposons que nous sachions que les changements qui ont causé la régression se trouvaient dans le répertoire com/workingDir alors nous pouvons exécuter git bisect start com/workingDir Cela signifie que seuls les commits qui ont modifié le contenu de ce répertoire seront vérifiés, et cela rend les choses encore plus rapides.

De plus, s'il est difficile de dire si un commit particulier est bon ou mauvais, vous pouvez pouvez lancer git bisect skip qui l'ignorera. S'il y a suffisamment d'autres git bisect utilisera un autre commits pour restreindre la recherche à la place.

0 votes

J'aime vraiment cela. Souvent, je sais déjà dans quel fichier se trouve un bogue, donc tout ce que je cherche est dans quel commit le bogue a été introduit.

16voto

Nabeel Ahmed Points 356

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

$ git bisect start # Je commence à bissecter

$ git bisect bad # indiquant que le commit actuel (v1.5) a le point de régression/réglage 'mauvais'.

$ git bisect good v1.0 # mentionnant qu'il s'agit du dernier bon commit fonctionnel (sans régression)

Cette mention des "mauvais" et des "bons" points permettra de git bisect (recherche binaire) choisissez l'élément du milieu (commit v1.3). Si la régression est présente au moment du commit v1.3, vous la définirez comme le nouveau "mauvais" point, c'est-à-dire ( Bon -> v1.0 et Mauvais -> v1.3 )

$ git bisect bad

ou de la même manière, si le commit v1.3 ne contient pas de bogue, vous le définirez comme le nouveau 'Bon point', c'est-à-dire (*Bon -> v1.3 et Mauvais -> v1.6).

$ git bisect good

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X