Niedawno stuknęło mi dwa lata doświaczenia w zawodzie programisty. Dwa lata to wystarczająco czasu aby zrozumieć, że rzeczywistość w branży IT znacząco różni się od ideałów, o których przeczytać mogłem w wielu książkach na temat programowania oraz zarządzania projektami. To działa na szczęście także w drugą stronę - pewnych zjawisk się nie spodziewałem, dopóki nie zobaczyłem ich na żywo, np. jak świetnie można się dogadywać i dzielić pracą w ramach zespołu. Na studiach z pracami w grupie bywało ciężko 😊.

Jednym z rozczarowań, które doświadczam na co dzień jest mała świadomość programistów co do pracy z gitem. Nie raz ani nie dwa byłem świadkiem sytuacji, kiedy programista zastanawiał się, czy nie popsuje niczego na serwerze modyfikując swojego lokalnego brancha albo np. że żeby wycofać zmiany z commita robi się git revert. Ostatnio miałem sytuację, kiedy to odnalazłem buga, który nie wiadomo kiedy został wprowadzony do kodu. Jak powiedziałem znajomemu, że znajdę commit w 10 minut (w zakresie commitów z około 2 miesięcy) to popatrzał się na mnie wielkimi oczyma... Jeszcze większe oczy zrobił, kiedy powiedziałem, że zrobię to za pomocą narzędzia dostępnego w gicie... To zmotywowało mnie do napisania tego wpisu.

Czym jest git bisect?

Git bisect (jak sama dokumentacja nam to wyjaśnia) jest narzędziem do znajdowania commitów, które spowodowały niepoprawne działanie aplikacji. Chociaż z reguły je tak używam, to z powodzeniem można to narzędzie wykorzystać do innych zastosowań, np. do znalezienia commita, w którym została dodana lub usunięta nowa funkcjonalność biznesowa (jeżeli nie możemy tego znaleźć np. w zadaniach na Jirze).

Jak używać git bisect?

Jak używać narzędzia pokażę na przykładzie. W moim przypadku (kod do ściągnięcia tutaj) mamy projekt aplikacji konsolowej z jedną funkcjonalnością: wypisanie na ekran Hello World. Projekt posiada 11 commitów, gdzie wiemy, że w pierwszym commicie funkcjonalność ta działa poprawnie, natomiast w ostatnim - wiemy, że na pewno nie działa (można to sprawdzić przełączając się na ostatnią rewizję i wykonując polecenie:

php -f index.php

Wynik:

Warning: file_get_contents(resources/messages.txt): failed to open stream: No such file or directory in C:\Users\bartl\bisect.example\src\Services\MessageService.php on line 11

Fatal error: Uncaught TypeError: Return value of App\Services\MessageService::getMessage() must be of the type string, boolean returned in C:\Users\bartl\bisect.example\src\Services\MessageService.php:11
Stack trace:
#0 C:\Users\bartl\bisect.example\index.php(11): App\Services\MessageService::getMessage()
#1 {main}
  thrown in C:\Users\bartl\bisect.example\src\Services\MessageService.php on line 11

Jeżeli próbowalibyśmy przełączać się ręcznie na każdy commit, to dojście do problematycznej rewizji zajęło by nam przełączenie się 5 razy (zakładając, że robilibyśmy to po kolei). Zaczynamy teraz zabawę z narzędziem.

Aby rozpocząć korzystanie z narzędzia wpisujemy komendę:

git bisect start

Następnie wskazujemy commit, który wiemy że jest wadliwy oraz commit, w którym aplikacja działa na pewno dobrze:

git bisect bad 3a394652dddd121bbc72e85af1204d7f751efa0d
git bisect good cf6d975869701869f36afebca30067049b822c89

otrzymujemy w następstwie komunikat:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[b055318f5ef1c4689755c4349ce886f5212884ee] Jira-05 Read message from file

narzędzie informuje nas, że maksymalna liczba commitów do sprawdzenia to 5 (od razu została przełączona rewizja do commita o nazwie Jira-05 Read message from file). Teraz musimy sprawdzić, czy w tym commicie jest wszystko ok. W konsoli uruchamiamy skrypt za pomocą polecenia (ponownie):

php -f index.php

i sprawdzamy jaki jest wynik... nie działa! Tak więc informujemy o tym narzędzie:

git bisect bad

Ok, ale to nie daje nam jednoznacznej odpowiedzi, który commit zepsuł działanie. Narzędzie informuje nas w komunikacie, że musimy zrobić więcej przełączeń:

Bisecting: 1 revision left to test after this (roughly 1 step)
[5ea7ad8c585de7f5abf8b7ff6c141c88f2cb0311] Jira-03 Add response code

sprawdzamy działanie:

php -f index.php

Działa! Mówimy teraz narzędziu, że jest ok:

git bisect good

To jednak nie daje nadal jednoznacznej odpowiedzi na pytanie, który commit jest wadliwy (jako pierwszy):

Bisecting: 0 revisions left to test after this (roughly 0 steps)
[d89101a6ae07801ab3163bf49f677496653043be] Jira-04 Add message.txt

sprawdzamy:

php -f index

Działa! Mówimy narzędziu, że jest ok. Dostajemy informację:

b055318f5ef1c4689755c4349ce886f5212884ee is the first bad commit
commit b055318f5ef1c4689755c4349ce886f5212884ee
Author: Bartłomiej Romanek <b.romanek@rombarte.pl>
Date:   Wed Feb 5 19:21:59 2020 +0100

    Jira-05 Read message from file

:100644 100644 1faf1706c4e21bb1b3eabcd5fa021525158e687d b102caef1750f3aef74f44df7ddae059d1bbdb8f M      index.php

no i mamy to! Winowajcą jest commit o nazwie Jira-05 Read message from file autorstwa Bartka Romanek. Ty gałganie!

Na koniec przywracamy stan sprzed użycia narzędzia:

git bisect reset

W powyższym przypadku liczba przełączeń nie została znacząco zoptymalizowana (3 zamiast 5 przełączeń), ale im więcej rewizji, tym z reguły większy zysk. Wynika to z tego, że narzędzie opiera się o algorytm wyszukiwania binarnego. No, ale ogromną zaletą jest na pewno łatwość przełączania rewizji - wystarczy polecenie git bisect good / git bisect bad i mamy automatyczne przełączenie na inną rewizję.

To najprostszy przypadek użycia tego narzędzia. Zapraszam do jego zgłębienia i do korzystania z niego na co dzień!