Wie benutzt man Git Bisect?

438

Ich habe einige Artikel gelesen, die sagen, dass git bisectdas großartig ist. Ich bin jedoch kein Muttersprachler und kann nicht verstehen, warum es großartig ist.

Könnte jemand bitte mit einem Codebeispiel demonstrieren:

  1. Wie benutzt man es?
  2. Ist es einfach so svn blame?
IAdapter
quelle
@ 01: Wie das Git-Buch sagt: Führen Sie eine Brute-Force-Suche durch die Projektgeschichte durch .
eckes
7
Nicht so /// brute :-), es verwendet die binäre Suche.
Cojocar
"Git-Schuld" ähnelt "SVN-Schuld". "git bisect" ist eine ganz andere Sache
William Pursell
Für das, was es wert ist, gibt es auch in Pro Git eine gute Beschreibung von Bisect . Sylvains Antwort ist ein weiterer guter Versuch. Wenn Sie nach all dem immer noch nichts verstehen, würde ich Ihnen vorschlagen, eine spezifischere Frage zu stellen. Allgemeine Fragen geben allgemeine Antworten.
Cascabel
4
aktualisierter Buchlink
gdelfino

Antworten:

655

Die Idee dahinter git bisectist, eine binäre Suche im Verlauf durchzuführen, um eine bestimmte Regression zu finden. Stellen Sie sich vor, Sie haben die folgende Entwicklungsgeschichte:

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

Sie wissen, dass Ihr Programm bei der currentRevision nicht ordnungsgemäß funktioniert und dass es bei der Revision funktioniert hat 0. So wurde die Regression wahrscheinlich in einer der Commits eingeführt 1, 2, 3, 4, 5, current.

Sie können versuchen, jedes Commit zu überprüfen, es zu erstellen und zu überprüfen, ob die Regression vorhanden ist oder nicht. Wenn es eine große Anzahl von Commits gibt, kann dies lange dauern. Dies ist eine lineare Suche. Wir können es besser machen, indem wir eine binäre Suche durchführen. Dies ist, was der git bisectBefehl tut. Bei jedem Schritt wird versucht, die Anzahl der möglicherweise fehlerhaften Revisionen um die Hälfte zu reduzieren.

Sie verwenden den folgenden Befehl:

$ 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

Nach diesem Befehl gitwird ein Commit ausgecheckt. In unserem Fall wird es festgeschrieben 3. Sie müssen Ihr Programm erstellen und prüfen, ob die Regression vorhanden ist oder nicht. Sie müssen auch gitden Status dieser Revision mit angeben, git bisect badob die Regression vorhanden ist oder git bisect goodnicht.

Nehmen wir an, dass die Regression in Commit eingeführt wurde 4. Dann ist die Regression in dieser Revision nicht vorhanden, und wir sagen es git.

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

Es wird dann ein weiteres Commit ausgecheckt. Entweder 4oder 5(da es nur zwei Commits gibt). Nehmen wir an, es hat ausgewählt 5. Nach einem Build testen wir das Programm und stellen fest, dass die Regression vorhanden ist. Wir sagen es dann zu git:

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

Wir testen die letzte Revision 4. Und da es derjenige ist, der die Regression eingeführt hat, sagen wir git:

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

In dieser einfachen Situation hatten wir nur zu Test 3 Versionen ( 3, 4, 5) statt 4 ( 1, 2, 3, 4). Dies ist ein kleiner Gewinn, aber das liegt daran, dass unsere Geschichte so klein ist. Wenn der Suchbereich N Commits umfasst, sollten wir erwarten, 1 + log2 N Commits mit git bisectstatt ungefähr N / 2 Commits mit einer linearen Suche zu testen .

Sobald Sie das Commit gefunden haben, das die Regression eingeführt hat, können Sie es untersuchen, um das Problem zu finden. Sobald dies erledigt ist, git bisect resetsetzen Sie alles wieder in den ursprünglichen Zustand zurück, bevor Sie den git bisectBefehl verwenden.

Sylvain Defresne
quelle
5
Ich werde hier kontrastieren, dies ist eine gute Erklärung für die Halbierung, aber es hilft mir wirklich nicht, sie zu verwenden. Zumal ich es geschafft habe, ein gutes Commit zu finden und jetzt in diesem Zweig bin. Von dieser Position aus ist diese Erklärung überhaupt keine Hilfe. Wie spezifiziere ich den fehlerhaften Zweig, ohne ihn
auszuchecken
4
Sie können git bisect bad <rev> [<rev>...]bestimmte Revisionen als schlecht (oder gut mit git bisect good <rev> [<rev>...]) markieren . revkann eine beliebige Revisionskennung sein, wie ein
Filialname
39
... und wenn Sie fertig sind, geben Sie ein git bisect reset, um alles wieder auf das letzte Commit zu setzen
Peetonn
17
Eine der großartigsten Antworten auf dem Stapel. Sehr gut artikuliert. Ich mache diesen Prozess seit Jahren manuell, indem ich einfach einen beliebigen halben Punkt zwischen einem guten und einem schlechten Commit auswähle, dann wieder zwischen diesem und dem guten / schlechten, je nachdem, ob es selbst gut / schlecht war. Es war schon immer ein großer Schmerz im Arsch und ich hatte bis heute noch nie von diesem Git-Unterbefehl gehört ... hahaha
Chev
3
@Nemoden, ja, das kann es, im Grunde kann es für jede Art von Projekt nützlich sein. Sie müssen nur den Schritt "Test machen" durch "Bereitstellen der Website und Reproduzieren des Problems"
ersetzen
158

git bisect run automatische Halbierung

Wenn Sie ein automatisiertes ./testSkript mit dem Beendigungsstatus 0 haben, wenn der Test in Ordnung ist, können Sie den Fehler automatisch finden mit 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

Dies setzt natürlich voraus, dass das Testskript, wenn ./testes git-verfolgt wird, bei einem früheren Festschreiben während der Halbierung nicht verschwindet.

Ich habe festgestellt, dass Sie sehr oft davonkommen können, indem Sie einfach das In-Tree-Skript aus dem Baum kopieren und möglicherweise mit PATHähnlichen Variablen spielen und es stattdessen von dort aus ausführen .

Wenn die Testinfrastruktur, von der testabhängig ist, von älteren Commits abbricht, gibt es natürlich keine Lösung, und Sie müssen die Dinge manuell ausführen und entscheiden, wie Commits einzeln getestet werden sollen.

Ich habe jedoch festgestellt, dass die Verwendung dieser Automatisierung häufig funktioniert und eine enorme Zeitersparnis für langsamere Tests in Ihrem Aufgabenstau bedeuten kann, bei denen Sie sie einfach über Nacht laufen lassen und möglicherweise Ihren Fehler bis zum nächsten Morgen identifizieren lassen können der Versuch.

Mehr Tipps

Bleiben Sie beim ersten fehlgeschlagenen Commit nach der Halbierung, anstatt zu Folgendem zurückzukehren master:

git bisect reset HEAD

start+ initial badund goodauf einmal:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

ist das gleiche wie:

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

Sehen Sie, was bisher getestet wurde (manuell goodund / badoder run):

git bisect log

Beispielausgabe:

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

Zeigen Sie gute und schlechte Refs im Git-Protokoll an, um eine bessere Vorstellung von der Zeit zu erhalten:

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

Dies zeigt nur Commits mit einem entsprechenden Ref, was das Rauschen stark reduziert, aber automatisch generierte Refs vom Typ enthält:

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

die uns sagen, welche Commits wir als gut oder schlecht markiert haben.

Betrachten Sie dieses Test-Repo, wenn Sie mit dem Befehl herumspielen möchten.

Das Scheitern ist schnell, der Erfolg ist langsam

Manchmal:

  • Fehler treten schnell auf, z. B. einer der ersten Testpausen
  • Der Erfolg dauert eine Weile, z. B. wenn der Test nicht bestanden wurde und alle anderen Tests, die uns nicht wichtig sind, folgen

In diesen Fällen, z. B. wenn der Fehler immer innerhalb von 5 Sekunden auftritt, und wenn wir faul sind, den Test genauer zu gestalten, als wir es wirklich sollten, können wir Folgendes verwenden timeout:

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

Dies funktioniert seit dem timeoutBeenden, 124während das Beenden test-commandfehlschlägt 1.

Magic Exit Status

git bisect run ist etwas wählerisch in Bezug auf Exit-Status:

  • Alles über 127 lässt die Halbierung scheitern mit etwas wie:

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

    Insbesondere führt ein C assert(0)zu a SIGABRTund verlässt mit Status 134, was sehr ärgerlich ist.

  • 125 ist magisch und lässt den Lauf überspringen git bisect skip.

    Damit soll das Überspringen defekter Builds aus nicht verwandten Gründen unterstützt werden.

Siehe man git-bisectfür die Details.

Vielleicht möchten Sie also etwas verwenden wie:

#!/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"

Getestet auf Git 2.16.1.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
7
Woher weiß git, dass Ihr neuer Test nicht verschwindet, wenn Sie zu einer vorherigen / schlechten Revision zurückkehren / halbieren (ohne Ihren neu geschriebenen Test)?
Thebjorn
8
@thebjorn Sie haben einen Punkt: Soweit ich weiß, muss sich der Test entweder auf einer externen ausführbaren Datei in PATH oder in einer nicht verfolgten Datei im Repo befinden. In vielen Fällen ist dies möglich: Legen Sie den Test in eine separate Datei, fügen Sie die erforderliche Testkesselplatte mit einer gut gestalteten test_script+ modularen Testsuite hinzu und führen Sie ihn aus der separaten Datei aus, während Sie ihn halbieren. Wenn Sie das Problem beheben, führen Sie den Test in die Haupttestsuite ein.
Ciro Santilli 法轮功 冠状 病 六四 事件 21
1
@CiroSantilli 视 事件 法轮功 纳米比亚 威 视 Entschuldigung, ich habe Ihre Bearbeitung unten rückgängig gemacht, da ich der Meinung bin, dass sie für Neulinge erklärender ist und Ihrer Antwort nur einen weiteren Punkt hinzufügt (keine exakte Antwort wie Ihre - also erwähnen Sie das, es ist um einen Punkt hinzuzufügen)
Nicks
1
Es gibt viele Möglichkeiten, mit 'git bisect run' etwas falsch zu machen - zum Beispiel zu sehen, wie ein gutes Commit durch eine schlechte Zusammenführung rückgängig gemacht wurde. Es kommt rein, raus, wieder rein und wieder raus und nur das letzte "raus" ist schlecht. Sie können jedoch jederzeit eine manuelle "Git-Halbierung" durchführen. Da es sich um eine Halbierung handelt, sind nur wenige Schritte erforderlich - z. B. 1024 Commits in 10 Schritten.
Kombinator
1
@combinatorist Sie haben Recht, dass es fehlschlagen könnte. Ich finde es bisect runbesonders nützlich, wenn der Test lange dauert und ich ziemlich sicher bin, dass das Testsystem nicht kaputt geht. Auf diese Weise kann ich es einfach im Hintergrund laufen lassen oder über Nacht, wenn es zu viele Ressourcen beansprucht, ohne die Zeit für den Wechsel des Gehirnkontexts zu verlieren.
Ciro Santilli 法轮功 冠状 病 六四 事件 11
124

TL; DR

Start:

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

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

Wiederholen:

Problem besteht noch?

  • Ja: $ git bisect bad
  • Nein: $ git bisect good

Ergebnis:

<abcdef> is the first bad commit

Wenn fertig:

git bisect reset
Geoffrey Hale
quelle
3
Stellen Sie sicher, dass Sie an der Wurzel Ihres Git-Repos sind, sonst erhalten Sie ein seltsames "Sie müssen diesen Befehl von der obersten Ebene des Arbeitsbaums aus ausführen." Error.
PR Whitehead
Also habe ich auf meinem HEAD schlecht und beim ersten Commit gut gemacht, als der Fehler nicht vorhanden war. Was ist als nächstes zu tun? Wenn der Fehler nicht vorhanden ist, ist es gut, zum nächsten Commit überzugehen.
Gobliins
@Gobliins Wenn kein Fehler vorliegt, korrigieren Sie ihn, git bisect goodum zum nächsten Commit zu gelangen.
Geoffrey Hale
40

Nur um einen weiteren Punkt hinzuzufügen:

Wir können einen Dateinamen oder einen Pfad angeben, git bisect startfalls wir wissen, dass der Fehler von bestimmten Dateien stammt. Angenommen, wir wussten, dass sich die Änderungen, die die Regression verursacht haben, im Verzeichnis com / workingDir befinden, dann können wir sie ausführen. git bisect start com/workingDirDies bedeutet, dass nur die Commits überprüft werden, die den Inhalt dieses Verzeichnisses geändert haben, und dies macht die Dinge noch schneller.

Wenn es schwierig ist zu erkennen, ob ein bestimmtes Commit gut oder schlecht ist, können Sie es ausführen git bisect skip, wodurch es ignoriert wird. Wenn es genügend andere Commits gibt, verwendet git bisect stattdessen eine andere, um die Suche einzugrenzen.

Nicks
quelle
15

$ git bisect ..Grundsätzlich ein Git-Tool zum Debuggen . 'Git Bisect' debuggt, indem die vorherigen Commits seit Ihrem letzten (bekannten) Arbeits-Commit durchlaufen werden . Es verwendet die binäre Suche, um alle diese Commits zu durchlaufen und zu dem zu gelangen, der die Regression / den Fehler eingeführt hat.

$ git bisect start # Starthalbierung

$ git bisect bad # besagt, dass das aktuelle Commit (v1.5) den Regressions- / Einstellungspunkt 'schlecht' hat

$ git bisect good v1.0 # Erwähnung des letzten guten Arbeitseinsatzes (ohne Regression)

Diese Erwähnung von 'schlechten' und 'guten' Punkten hilft Git Bisect (binäre Suche), das mittlere Element auszuwählen (Commit v1.3). Wenn die Regression bei Commit v1.3 vorhanden ist, setzen Sie sie als neuen "schlechten" Punkt, dh ( Gut -> v1.0 und Schlecht -> v1.3 ).

$ git bisect bad

oder ähnlich, wenn das Commit v1.3 fehlerfrei ist, setzen Sie es als neuen 'guten Punkt', dh (* Gut -> v1.3 und schlecht -> v1.6).

$ git bisect good
Nabeel Ahmed
quelle
2

Hinweis: Die Begriffe goodund badsind nicht die einzigen , die Sie verpflichten zur Markierung mit oder ohne eine bestimmte Eigenschaft nutzen können.

Git 2.7 (Q4 2015) führte neue git bisectOptionen ein.

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

Mit Dokumentation hinzufügen:

Manchmal suchen Sie nicht nach dem Commit, das einen Bruch verursacht hat, sondern nach einem Commit, das einen Wechsel zwischen einem anderen "alten" und einem "neuen" Zustand verursacht hat .

Beispielsweise suchen Sie möglicherweise nach dem Commit, mit dem ein bestimmter Fix eingeführt wurde.
Oder Sie suchen nach dem ersten Commit, bei dem die Quellcode-Dateinamen schließlich alle in den Namensstandard Ihres Unternehmens konvertiert wurden. Oder Wasauchimmer.

In solchen Fällen kann es sehr verwirrend sein, die Begriffe "gut" und "schlecht" zu verwenden, um sich auf "den Zustand vor der Änderung" und "den Zustand nach der Änderung" zu beziehen.

Sie können stattdessen die Begriffe " old" und " new" anstelle von " good" und " bad" verwenden.
(Beachten Sie jedoch, dass Sie " good" und " bad" nicht mit " old" und " new" in einer einzigen Sitzung mischen können .)

In dieser allgemeineren Verwendung geben Sie git bisectein newCommit mit einer Eigenschaft und ein oldCommit ohne diese Eigenschaft an.

Jedes Mal git bisect, wenn Sie ein Commit auschecken, testen Sie, ob dieses Commit die Eigenschaft hat:
Wenn dies der Fall ist, markieren Sie das Commit als " new"; Andernfalls markieren Sie es als " old".

Wenn die Halbierung abgeschlossen ist, git bisectwird gemeldet, durch welches Commit die Eigenschaft eingeführt wurde.


Siehe Commit 06e6a74 , Commit 21b55e3 , Commit fe67687 (29. Juni 2015) von Matthieu Moy ( moy) .
Siehe Commit 21e5cfd (29. Juni 2015) von Antoine Delaite ( CanardChouChinois) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 22dd6eb , 05. Oktober 2015)

VonC
quelle