Jak korzystać z git bisect?

438

Przeczytałem kilka artykułów, które mówią, że git bisectto niesamowite. Nie jestem jednak native speakerem i nie rozumiem, dlaczego jest niesamowity.

Czy ktoś mógłby zademonstrować za pomocą próbki kodu:

  1. Jak tego użyć?
  2. Czy to tak po prostu svn blame?
IAdapter
źródło
@ 01: Jak mówi księga gitów: przeszukaj brutalnie historię projektu .
eckes
7
Nie tak /// brutalny :-), używa wyszukiwania binarnego.
cojocar
„git blame” jest podobny do „svn blame”. „git bisect” to coś zupełnie innego
William Pursell
Dla tego, co jest warte, w Pro Git jest też dobry opis bisecta . Odpowiedź Sylvaina to kolejny dobry strzał. Jeśli po przeanalizowaniu tego wszystkiego nadal nie rozumiesz, radzę zadać bardziej szczegółowe pytanie. Pytania ogólne rodzą ogólne odpowiedzi.
Cascabel,
4
zaktualizowany link do książki: git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search
gdelfino

Odpowiedzi:

655

Ideą git bisectjest przeszukiwanie binarne w historii w celu znalezienia określonej regresji. Wyobraź sobie, że masz następującą historię rozwoju:

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

Wiesz, że twój program nie działa poprawnie przy currentwersji i że działał przy wersji 0. Więc regresji prawdopodobne wprowadzone w jednym z zatwierdzeń 1, 2, 3, 4, 5, current.

Możesz spróbować sprawdzić każde zatwierdzenie, zbudować je, sprawdzić, czy regresja jest obecna, czy nie. Jeśli istnieje duża liczba zatwierdzeń, może to zająć dużo czasu. To jest wyszukiwanie liniowe. Możemy to zrobić lepiej, przeprowadzając wyszukiwanie binarne. To właśnie git bisectrobi polecenie. Na każdym kroku stara się zmniejszyć o połowę liczbę poprawek, które są potencjalnie złe.

Użyjesz następującego polecenia:

$ 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

Po tym poleceniu gitkasuje zatwierdzenie. W naszym przypadku zostanie zatwierdzone 3. Musisz zbudować swój program i sprawdzić, czy regresja jest obecna. Musisz także poinformować gito statusie tej wersji, git bisect badczy regresja jest obecna, czy git bisect goodnie.

Załóżmy, że regresja została wprowadzona w zatwierdzeniu 4. Zatem regresja nie jest obecna w tej wersji i mówimy o tym git.

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

Następnie przejdzie do kolejnego zatwierdzenia. Albo 4albo 5(ponieważ są tylko dwa zatwierdzenia). Załóżmy, że wybrał 5. Po kompilacji testujemy program i widzimy, że regresja jest obecna. Następnie mówimy, aby git:

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

Testujemy ostatniej wersji, 4. A ponieważ to on wprowadził regresję, mówimy, aby git:

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

W tej prostej sytuacji, mieliśmy tylko do testu 3 wersje ( 3, 4, 5) zamiast 4 ( 1, 2, 3, 4). To niewielka wygrana, ale dzieje się tak, ponieważ nasza historia jest tak mała. Jeśli zakres wyszukiwania wynosi N zatwierdzeń, powinniśmy spodziewać się przetestowania 1 + log2 N zatwierdzeń git bisectzamiast z grubsza N / 2 zatwierdzeń przy wyszukiwaniu liniowym.

Po znalezieniu zatwierdzenia, które wprowadziło regresję, możesz przestudiować go, aby znaleźć problem. Po wykonaniu tej czynności należy git bisect resetprzywrócić wszystko do pierwotnego stanu przed użyciem git bisectpolecenia.

Sylvain Defresne
źródło
5
Mam zamiar tu się sprzeciwić, to dobre wytłumaczenie bisecta, ale tak naprawdę nie pomaga mi z niego korzystać. Zwłaszcza, że ​​udało mi się znaleźć dobre zatwierdzenie i jestem teraz w tej gałęzi. Z tej pozycji to wyjaśnienie nie jest wcale pomocne. Jak określić zły oddział bez sprawdzania go, na przykład
PandaWood
4
Możesz użyć, git bisect bad <rev> [<rev>...]aby oznaczyć określone wersje jako złe (lub dobre z git bisect good <rev> [<rev>...]). revmoże być dowolnym identyfikatorem zmiany, takim jak nazwa oddziału, tag, skrót zatwierdzenia (lub unikalny prefiks skrótu zatwierdzenia), ...
Sylvain Defresne
39
... a kiedy skończysz, git bisect reset
piszesz
17
Jedna z najbardziej niesamowitych odpowiedzi na stosie. Bardzo dobrze artykułowany. Od lat robię ten proces ręcznie, po prostu wybierając dowolny punkt pośredni między dobrym a złym zatwierdzeniem, a następnie ponownie między tym a dobrym / złym, w zależności od tego, czy sam był dobry / zły. Zawsze był to ogromny ból w dupie i do tej pory nigdy nie słyszałem o tej komendzie git ... hahaha
Chev
3
@Nemoden, tak, może, w zasadzie może być użyteczny dla każdego rodzaju projektu. Trzeba tylko zastąpić krok „wykonaj test”
słowem
159

git bisect run automatyczna dwusieczna

Jeśli masz zautomatyzowany ./testskrypt, który ma status wyjścia 0, jeśli test jest OK, możesz automatycznie znaleźć błąd za pomocą 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

To oczywiście zakłada, że ​​jeśli skrypt testowy ./testjest śledzony przez git, to nie znika po wcześniejszym zatwierdzeniu podczas bisekcji.

Przekonałem się, że bardzo często możesz uciec, po prostu kopiując skrypt z drzewa z drzewa i prawdopodobnie bawiąc się PATHzmiennymi podobnymi do niego i uruchamiając go stamtąd.

Oczywiście, jeśli infrastruktura testowa, od której testzależy test, psuje się na starszych zatwierdzeniach, nie ma rozwiązania i trzeba będzie wykonać czynności ręcznie, decydując, jak testować zatwierdzenia jeden po drugim.

Przekonałem się jednak, że korzystanie z tej automatyzacji często działa i może być ogromną oszczędnością czasu dla wolniejszych testów leżących w zaległych zadaniach, gdzie możesz po prostu pozwolić jej działać przez noc i być może wykryć swój błąd do następnego ranka, warto próba.

Więcej porad

Pozostań przy pierwszym nieudanym zatwierdzeniu po bisecta zamiast wracać do master:

git bisect reset HEAD

start+ inicjał badi goodza jednym razem:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

jest taki sam jak:

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

Zobacz, co zostało przetestowane do tej pory (ręcznie goodi badlub run):

git bisect log

Przykładowe dane wyjściowe:

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

Pokaż dobre i złe referencje w dzienniku git, aby uzyskać lepszy obraz czasu:

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

To pokazuje tylko zatwierdzenia z odpowiednim ref, co zmniejsza szarość, ale obejmuje automatycznie generowane ref:

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

które mówią nam, które zobowiązania oznaczyliśmy jako dobre lub złe.

Rozważ to testowe repozytorium, jeśli chcesz pobawić się poleceniem.

Awaria jest szybka, sukces powolna

Czasami:

  • awaria zdarza się szybko, np. jeden z pierwszych testów się psuje
  • sukces zajmuje trochę czasu, np. zepsuty test zdaje, a wszystkie inne testy, których nie obchodzi nas przestrzeganie

W takich przypadkach, np. Zakładając, że awaria zawsze zdarza się w ciągu 5 sekund, a jeśli jesteśmy leniwi, aby uczynić test bardziej szczegółowym, tak jak powinniśmy, możemy użyć timeoutjak w:

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

Działa to od timeoutwyjścia, 124podczas gdy awaria test-commandwyjść 1.

Magiczne statusy wyjścia

git bisect run jest nieco wybredny co do statusów wyjścia:

  • cokolwiek powyżej 127 powoduje, że bisekcja zawodzi z czymś takim jak:

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

    W szczególności C assert(0)prowadzi do a SIGABRTi wychodzi ze statusem 134, co jest bardzo denerwujące.

  • 125 to magia i sprawia, że ​​bieg jest pomijany git bisect skip.

    Ma to na celu pomóc w pomijaniu uszkodzonych wersji z niepowiązanych powodów.

Zobacz man git-bisectszczegóły.

Więc możesz chcieć użyć czegoś takiego:

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

Testowane na git 2.16.1.

Ciro Santilli
źródło
7
Skąd git wie, aby nie znikać z twojego nowego testu podczas przywracania / dzielenia z powrotem na poprzednią / złą wersję (która nie miała twojego nowo napisanego testu)?
thebjorn
8
@ thebjorn masz rację: o ile mi wiadomo, albo test musi być na zewnętrznym pliku wykonywalnym w PATH, albo w nieśledzonym pliku w repozytorium. W wielu przypadkach jest to możliwe: umieść test na osobnym pliku, dołącz niezbędną płytkę testową z dobrze spreparowanym test_script+ modułowym pakietem testowym i uruchom ją z oddzielnego pliku podczas dzielenia na części. Po naprawieniu połącz test z głównym zestawem testów.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 六四 事件 法轮功 纳米比亚 威 视 Przepraszam, cofnąłem twoją edycję poniżej, ponieważ uważam, że jest to bardziej wyjaśniające dla początkujących i tylko dodaje kolejny punkt do twojej odpowiedzi (nie jest to dokładna odpowiedź jako twoja - więc wspominając o tym dodać punkt)
Nicks
1
Istnieje wiele sposobów, aby pójść nie tak z „git bisect run” - na przykład zobaczyć, jak dobre zatwierdzenie zostało odwrócone przez złe scalenie. Wchodzi, wychodzi, wraca i wychodzi ponownie i tylko ostatnie „wyjście” jest złe. Zawsze możesz jednak wykonać ręczne polecenie „git bisect”. Ponieważ jest to dwusieczna, zajmuje tylko kilka kroków - np. 1024 zatwierdza w 10 krokach.
kombinatorator
1
@combinatorist masz rację, że może się nie powieść. Uważam, że jest bisect runszczególnie przydatny, gdy test trwa długo, i jestem pewien, że system testowy się nie zepsuje. W ten sposób mogę po prostu pozostawić go uruchomionego w tle lub na noc, jeśli zajmuje on zbyt wiele zasobów, bez utraty czasu przełączania kontekstu mózgowego.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
124

TL; DR

Początek:

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

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

Powtarzać:

Problem nadal istnieje?

  • Tak: $ git bisect bad
  • Nie: $ git bisect good

Wynik:

<abcdef> is the first bad commit

Po zakończeniu:

git bisect reset
Geoffrey Hale
źródło
3
Upewnij się, że jesteś u podstaw repozytorium git, inaczej dostaniesz dziwne „Musisz uruchomić to polecenie z najwyższego poziomu działającego drzewa”. błąd.
PR Whitehead
Więc źle zrobiłem na mojej HEAD i dobrze przy pierwszym zatwierdzeniu, gdy błąd nie był obecny. Co dalej? kiedy błąd nie jest obecny git bisect dobrze przejść do następnego zatwierdzenia?
Gobliiny
@Gobliins Jeśli błąd nie występuje, popraw, git bisect goodaby przejść do następnego zatwierdzenia.
Geoffrey Hale,
40

Aby dodać kolejny punkt:

Możemy podać nazwę pliku lub ścieżkę, na git bisect startwypadek gdybyśmy wiedzieli, że błąd pochodzi z określonych plików. Załóżmy na przykład, że wiemy, że zmiany, które spowodowały regresję, znajdowały się w katalogu com / workingDir, a następnie możemy uruchomić. git bisect start com/workingDirOznacza to, że sprawdzane będą tylko zmiany, które zmieniły zawartość tego katalogu, a to przyspieszy.

Ponadto, jeśli trudno jest stwierdzić, czy dane zatwierdzenie jest dobre, czy złe, możesz uruchomić git bisect skip, co go zignoruje. Biorąc pod uwagę, że istnieje wystarczająca liczba innych zatwierdzeń, git bisect użyje innego, aby zawęzić wyszukiwanie.

Nicki
źródło
15

$ git bisect ..w zasadzie narzędzie Git do debugowania . Debuguje „Git Bisect”, przechodząc przez poprzednie zatwierdzenia od ostatniego (znanego) działania zatwierdzenia. Korzysta z wyszukiwania binarnego, aby przejrzeć wszystkie zatwierdzenia, aby dostać się do tego, który wprowadził regresję / błąd.

$ git bisect start # Początkowa dwusieczna

$ git bisect bad # stwierdzając, że bieżące zatwierdzenie (wersja 1.5) ma punkt „regresji / ustawienia” zły

$ git bisect good v1.0 # wspominając o ostatnim dobrym działaniu zatwierdzającym (bez regresji)

Ta wzmianka o „złych” i „dobrych” punktach pomoże git bisect (wyszukiwanie binarne) wybrać środkowy element (commit v1.3). Jeśli regresja jest dostępna w wersji 1.3, ustawisz ją jako nowy „zły” punkt, tj. ( Dobra -> v1.0 i Zła -> v1.3 )

$ git bisect bad

lub podobnie, jeśli zatwierdzenie w wersji 1.3 jest wolne od błędów, ustawisz go jako nowy „Dobry punkt”, tj. (* Dobry -> v1.3 i Zły -> v1.6).

$ git bisect good
Nabeel Ahmed
źródło
2

Uwaga: warunki goodi badnie są jedynymi, których możesz użyć do oznaczenia zatwierdzenia z określoną właściwością lub bez niej.

Git 2.7 (IV kwartał 2015 r.) Wprowadził nowe git bisectopcje.

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

Z dodaniem dokumentacji:

Czasami nie szukasz zatwierdzenia, które wprowadziło złamanie, ale raczej zatwierdzenia, które spowodowało zmianę między innym „starym” stanem a „nowym” stanem .

Na przykład możesz szukać zatwierdzenia, które wprowadziło określoną poprawkę.
Być może szukasz pierwszego zatwierdzenia, w którym nazwy plików kodu źródłowego zostały w końcu przekonwertowane na standard nazewnictwa Twojej firmy. Lub cokolwiek.

W takich przypadkach użycie terminów „dobry” i „zły” może być bardzo mylące w odniesieniu do „stanu przed zmianą” i „stanu po zmianie”.

Zamiast tego możesz użyć odpowiednio terminów „ old” i „ new” zamiast „ good” i „ bad”.
(Pamiętaj jednak, że nie można łączyć „ good” i „ bad” z „ old” i „ new” w jednej sesji).

W tym bardziej ogólnym zastosowaniu, zatwierdzenie git bisectnew” ma pewną właściwość i oldzatwierdzenie, które nie ma tej właściwości.

Za każdym razem, gdy git bisectsprawdzasz zatwierdzenie, sprawdzasz, czy to zatwierdzenie ma właściwość:
Jeśli tak, zaznacz zatwierdzenie jako „ new”; w przeciwnym razie zaznacz to jako „ old”.

Po bisekcji git bisectzgłosi, które zatwierdzenie wprowadziło właściwość.


Zobacz commit 06e6a74 , commit 21b55e3 , commit fe67687 (29 cze 2015) autor: Matthieu Moy ( moy) .
Zobacz commit 21e5cfd (29 czerwca 2015) autor: Antoine Delaite ( CanardChouChinois) .
(Scalone przez Junio ​​C Hamano - gitster- w commit 22dd6eb , 05 paź 2015)

VonC
źródło