CLI AI

git bisect: найти коммит, который всё сломал

2026-07-20

git bisect start && git bisect bad && git bisect good <старый-хеш> запускает двоичный поиск по истории, а git bisect run ./check.sh доводит его до конца без единого ответа руками. Решает код возврата скрипта: 0 — коммит хороший, ненулевой — плохой, 125 — «этот коммит проверить нельзя, пропусти».

1. Обозначить границы

clai
$ clai начни искать коммит, который сломал сложение, хороший коммит fa5873a→ git bisect start && git bisect bad && git bisect good fa5873aBisecting: 4 revisions left to test after this (roughly 2 steps)[37a14229ef73e1801a49defb16a86a26c122aa41] refactor step 5

bad без аргумента означает текущий HEAD. Git сразу переключается на середину диапазона и сообщает, сколько шагов осталось: десять коммитов — три-четыре проверки, тысяча — десять.

2. Отвечать на каждом шаге

clai
$ clai этот коммит рабочий→ git bisect goodBisecting: 2 revisions left to test after this (roughly 1 step)[cbae02ada1b9e211691957bc7daff38a214a5a13] speed up addition

Проверили, ответили good или bad, git переключился дальше. Каждый ответ отсекает половину оставшегося.

3. Ответ

clai
$ clai этот коммит сломан→ git bisect badcbae02ada1b9e211691957bc7daff38a214a5a13 is the first bad commitcommit cbae02ada1b9e211691957bc7daff38a214a5a13Author: Demo <[email protected]>Date:   Thu Aug 27 22:52:21 2026 +0700    speed up addition calc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

Git называет первый плохой коммит и сразу печатает его дифф-статистику. Одна команда git show <хеш> — и выясняется, что «ускорение сложения» заменило плюс на минус.

4. То же самое без единого ответа руками

clai
$ clai найди сломавший коммит автоматически, проверка в ./check.sh→ git bisect run ./check.shrunning './check.sh'Bisecting: 0 revisions left to test after this (roughly 0 steps)[e0991601aa7cd6c25d3ac78b486319a7ee55713e] refactor step 6running './check.sh'cbae02ada1b9e211691957bc7daff38a214a5a13 is the first bad commitbisect found first bad commit

Вот ради чего всё затевалось. Скрипт возвращает 0 для хорошего коммита и что угодно ненулевое для плохого, и он вполне может быть однострочником: test "$(./calc.sh 2 3)" = "5".

5. Коммиты, которые даже не собираются

clai
$ clai пропускай коммиты, которые не собираются→ git bisect run ./check125.sh

Код возврата 125 особый: он означает «про этот коммит ничего сказать нельзя, пропусти». Такой шаг появляется в git bisect log строкой # skip:. Обычная схема: сборка упала — exit 125, тест упал — exit 1.

6. Вернуться туда, откуда начали

clai
$ clai заверши бисект и верни меня на мою ветку→ git bisect resetPrevious HEAD position was cbae02a speed up additionSwitched to branch 'main'

Во время бисекта репозиторий находится в состоянии detached HEAD. reset возвращает на ветку, с которой начинали. Забудете — потом весь вечер будете гадать, почему в рабочей копии старый код.

Gotchas

  • Один неверный ответ портит весь прогон. Проверено на живом репозитории: ответ bad вместо good на одном шаге — и git обвинил e099160 вместо настоящего виновника cbae02a. Лечится через git bisect log: сохранить, поправить неверную строку и прогнать заново командой git bisect replay.
  • git bisect run читает коды возврата, а не вывод. Скрипт, который печатает «FAILED» и выходит с нулём, считается успехом. Код больше 127 прерывает бисект целиком.
  • Скрипт проверки держите вне репозитория. Неотслеживаемый файл переживает переключения коммитов, а скрипт, который существует только в свежих коммитах, исчезнет при первом же checkout на старый — вместе с вашим бисектом.

Related questions

А если хорошего коммита я не знаю? Возьмите заведомо старый: git bisect good HEAD~200. Если он тоже окажется сломанным, git скажет об этом на первом же шаге.

Можно бисектить по строке в выводе? Да, любой командой: git bisect run sh -c '! ./app 2>&1 | grep -q "NullPointer"'. Важно только, чтобы 0 означал «хорошо».

Как посмотреть, что изменил найденный коммит? git show <хеш> печатает дифф целиком, git show --stat <хеш> — только список файлов.

See also

CliAI пишет команды бисекта и однострочный скрипт проверки по обычному описанию и показывает их до запуска. Поставить одной строкой.