Дебаггинг с использованием git bisect

Перевод статьи «Debug your code using git bisect».

Несколько дней назад я впервые прочитал о git bisect в этом твите:

https://twitter.com/kefimochi/status/1259656631291277312
«Git bisect» очень крут! Вот как я это понимаю (вкратце):
Git bisect полезен, когда вы не можете легко отследить, какой именно кусок кода связан с появлением бага. Этот инструмент запускает двоичный поиск по всем коммитам между коммитом в хорошем состоянии (без багов) и коммитом в плохом состоянии (там, где обнаружен баг)».

Я с первого взгляда понял, что это просто потрясающий инструмент. Прямо с main-страницы, где говорится:

«git-bisect — используй двоичный поиск, чтобы найти коммит, с которым в базу попал баг».

(Прим. перев.: слово «bisect» означает «делить пополам»).

В общем, это же именно то, что нам нужно в нашей ежедневной работе, правда? Занимаясь разработкой какой-нибудь фичи, мы часто оказываемся в ситуации, когда отдельный ее фрагмент сломан. Но он не должен быть сломан, ведь мы же не трогали никакой код, имеющий отношение к этому фрагменту (или забыли об этом — что более вероятно). Что мы делаем в таких случаях? Лично я проверяю отдельные коммиты, которые мне кажутся подозрительными, или изменения в файлах. Но чтобы сузить поиск, нужно приложить чертовски много усилий. Для автоматизации процесса мы можем использовать git bisect.

Эта команда использует алгоритм двоичного поиска, чтобы найти в нашей истории коммитов именно тот, с которым появился баг.

Для начала мы должны указать, какой коммит является «плохим» («bad»), т. е., как мы знаем, точно содержит баг. В большинстве случаев он там, где HEAD, но мы можем указать и другой — при помощи commit hash.

Затем нам нужно пометить «хороший» («good») коммит — тот, в котором этого бага точно нет и все работает прекрасно.

Если вы помните технику двоичного поиска, вы также помните, что там используется самый маленький и самый большой индекс. Здесь роль этих индексов играют «плохой» и «хороший» коммит. После того как мы их определили, git bisect выбирает коммит между этими крайними точками и спрашивает, является он «плохим» или «хорошим». Таким образом диапазон продолжает сужаться, пока мы не находим именно тот коммит, с которым появилась проблема.

Фактически git bisect может быть использован для поиска коммита, изменившего любое свойство в проекте. Например, коммита с исправлением бага или коммита, после которого существенно улучшились показатели. Для подобного более широкого использования вместо терминов «good» и «bad» можно применять термины «old» и «new» («старый» и «новый»).

Photo by henry perks on Unsplash

Как использовать git bisect?

Для начала нам нужно начать bisect-сессию и пометить хороший и плохой коммит, т. е., определить границы, в которых мы будем искать баг.

# startup git bisect
$ git bisect start

# give git a commit where there is not a bug
$ git bisect good <commit-hash>

# give git a commit where there is a bug
$ git bisect bad <commit-hash>

Bisecting: X revisions left to test after this (roughly Y steps)
[commit-hash] <commit-message>

После этого процесс будет запущен автоматически. Git начнет делить ревизии надвое и загружать их для нас. Он будет проверять каждую ревизию (revision) и спрашивать нас, хороший это коммит или плохой. Чтобы вынести свой вердикт, нам нужно будет скомпилировать и протестировать код этой конкретной версии. Наш ответ будет выглядеть так:

# If that version works correctly, type
$ git bisect good

# If that version is broken, type
$ git bisect bad

# Then git bisect will respond with something like
Bisecting: 12 revisions left to test after this (roughly 4 steps)

После каждого круга будет применяться двоичный поиск, в результате чего «виновный» коммит будет найден очень быстро. То, насколько долго будет длиться вся процедура, зависит от количества коммитов между крайними точками (между «плохим» и «хорошим» коммитом). Но это все равно быстрее, чем проверять каждый коммит отдельно. Грубо говоря, на это уйдет log(revisionCount), то есть, Y = log(X), где Х — общее число ревизий, а Y — число итераций, которые могут понадобиться для поиска коммита с багом (в наихудшем случае).

Этот процесс продолжается итеративно: компиляция, тестирование, пометка коммита «плохим» или «хорошим» и переход на новый круг. В конечном итоге, когда у нас уже не останется ревизий для проверки, команда выведет описание первого «плохого» коммита.

Предположим, мы хотим найти коммит, который сломал фичу. Известно, что в промежутке между коммитами 8c054db и 5908ecf фича работала. Запустив сессию git bisect, мы указываем «хороший» и «плохой» коммит:

$ git bisect start
$ git bisect good 5908ecf
$ git bisect bad 8c054db

В своей консоли вы увидите нечто подобное:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[88f2ada15e8b2890b618e0300134deff9e4050c6] Add typography fixes

Здесь я протестировал свой код, и оказалось, что он работает хорошо. Помечаем его как хороший:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[88f2ada15e8b2890b618e0300134deff9e4050c6] Add typography fixes

$ git bisect good

Это приводит нас к следующему шагу:

Bisecting: 2 revisions left to test after this (roughly 1 step)
[aa91d493c90a854b78a02fb10b8c097f323b9d4a] 1.1.0

Тестирование показало, что этот код не работает, как должно. Вводим git bisect bad. В итоге будет выведено следующее:

aa91d493c90a854b78a02fb10b8c097f323b9d4a is the first bad commit
commit aa91d493c90a854b78a02fb10b8c097f323b9d4a
Author: uraniumreza <reza.uranium@gmail.com>
Date: Sun May 10 19:37:40 2020 +0600

1.1.0

src/helper/validation.js | 9 +++
src/landing/components/ContactUs.js | 52 +++++++++++++ - -
src/landing/service/api.js | 12 +++
3 files changed, 73 insertions(+), 11 deletions(-)

По окончании bisect-сессии, то есть, когда вы успешно нашли баг, нужно очистить состояние bisect и вернутся к оригинальному HEAD. Для этого запускаем команду:

$ git bisect reset

По умолчанию это вернет наше дерево к последнему подтвержденному коммиту перед запуском git bisect start. Но мы можем вернуться и к другому коммиту, если добавить опциональный аргумент:

$ git bisect reset <commit-hash>

Например, git bisect reset bisect/bad — переход к первой «плохой» ревизии. А если мы введем git bisect reset HEAD, то вообще не будем никуда переходить и останемся на текущем коммите.

Можно ли автоматизировать этот процесс?

Описанный процесс мы производим вручную, автоматизация здесь касается лишь эффективного сужения диапазона для поиска. Но мы можем запускать git bisect со скриптом (если у нас есть скрипт, умеющий определять, хороший у нас коммит или плохой. Это позволяет автоматизировать весь процесс компиляции, тестирования и пометки коммитов как «хороших» или «плохих».

# git bisect run <cmd> …
$ git bisect run node bisect/index.js

Примечание. Такой скрипт должен иметь код выхода 0, если текущий исходный код хороший/старый (good/old). Если исходный код плохой/новый (bad/new), код выхода должен быть в диапазоне чисел от 1 до 127 (включительно), кроме 125.

Заключение

Для облегчения жизни разработчика создано множество различных инструментов. Я уверен, что умение отлаживать код — особый навык, который должен быть в арсенале каждого разработчика. Надеюсь, моя статья поможет вам научиться работать с таким прекрасным инструментом отладки как git bisect. А если захотите узнать о нем больше, нужные сведения можно найти в документации.

[customscript]techrocks_custom_after_post_html[/customscript]

[customscript]techrocks_custom_script[/customscript]

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх