Perełki CLI: git bisect
Każdy programista używa takich narzędzi, z którymi pracuje mu się najlepiej, czasami organizacje narzucają je z góry. Niemniej, nagminnie obserwowałem, jak programiści, skupiając się na zaawansowanych i skomplikowanych narzędziach, często pomijali pewne mniej rozpowszechnione funkcje, które w teorii są wszystkim znane i każdy wie, do czego służą. Jedną z nich jest polecenie gita: git bisect.
git bisect - czyli jak szybko znaleźć dziurę w kodzie
Podczas życia projektów informatycznych tworzy się na prawdę dużo commitów. Bardzo, bardzo dużo :) W niektórych etapach, kiedy bardzo dużo kodu jest tworzone od nowa lub zmieniane, stosunkowo łatwo (mimo procesu testowego) wprowadzić błąd..
W przypadku, gdy bug nie jest oczywisty, jednym z podejść jest znalezienie momentu życia projektu, w którym go wprowadzono. Zamiast analizować setki commitów, a co za tym idzie, gigantyczną ilość kodu, wystarczy sprawdzić jeden.
I teraz przechodzimy do podstawowej trudności: Jak w efektywny sposób znaleźć commit, który wprowadził błąd? Naiwne podejście to znalezienie jakiegoś starego, działającego commita i powolne przesuwanie się commit po commicie, aż w koncu natrafimy na usterkę. Tutaj pojawia się pewien problem - jeśli np. wybraliśmy przedział ok. 300 commitów, czas potrzebny na weryfikacje może być naprawdę długi. W grę wchodzi powtórzenie procesu weryfikacji 300 razy, co może być traumatycznym przeżyciem dla osoby testującej. By usprawnić proces, można użyć skryptu w bash i wywołania testu automatycznego (jeśli go mamy). I tutaj pojawia się proste, a jednocześnie niesamowicie efektywne narzędzie, jakim jest git bisect.
Jak to działa?
Idea działania tego narzędzia jest prosta i nawiązuje do matematycznego algorytmu szukania miejsc zerowych funkcji ciągłych (https://pl.wikipedia.org/wiki/Metoda_r%C3%B3wnego_podzia%C5%82u). Innymi słowy, dzielimy przedział na pół, sprawdzamy czy błąd występuje w połowie. Jeśli występuje - powtarzamy algorytm w przedziale commitów wcześniejszych niż bieżący commit. Jeśli nie występuje, szukamy w późniejszych. Algorytm kończy się, gdy nasz przedział będzie jednoelementowy. Warto zwrócić uwagę, że ten algorytm znajduje błąd w log_2(N) krokach. Dla przykładu, jeśli mamy do sprawdzenia 300 commitów, znajdziemy bug w 9 krokach.
Ok, przejdźmy do przykładu. Załóżmy, że nie mamy automatycznego testu, który zweryfikowałby problem i robimy to ręcznie. Przykładowa sesja z git bisect wygląda następująco:
# zaczynamy od wyznaczenia zakresów $ git bisect start $ git bisect bad # bieżący commit jest zły $ git bisect good da8d6c0d81b629 # oznaczamy stary, poprawny commit Bisecting: 66 revisions left to test after this (roughly 6 steps) [b7161b5b98b6b2fd46e0e5c0472f3949b9f25a7f] refs: #2630 | Opis commita # wykonujemy test ręcznie - commit zły $ git bisect bad Bisecting: 32 revisions left to test after this (roughly 5 steps) [dfa7f15eed8236b732844124c83b5711f7167d46] Kolejny commit # sesja odpowiedzi git bisect good i git bisect bad w zależności od wyników testu... ... Wynik wyszukiwania: b7161b5b98b6b2fd46e0e5c0472f3949b9f25a7f is the first bad commit commit b7161b5b98b6b2fd46e0e5c0472f3949b9f25a7f Author: Jon Smith Date: Thu May 3 10:58:45 2018 +0200 refs: #2630 | Bad commit description.
Jak widać, w 6 krokach udało się przeskanować przedział 70 commitów.
Automatyzacja
Jeśli posiadamy automatyczne testy, git bisect może je wykorzystać. W tym celu, po oznaczeniu pierwotnego przedziału, wystarczy użyć opcji run: git bisect run ./phpunit.sh.
Przygotowanie takiego skryptu zostawiam jako zadanie domowe :)
z ambitnymi ludźmi i projektami