↓Перейти к содержанию
  1. Git: руководства и команды/

git bisect: поиск бага в истории коммитов

·9 минут·

Что такое git bisect и зачем он нужен #

Представьте ситуацию: вы разрабатываете проект с историей в 500+ коммитов, и вдруг выясняется, что фича, которая работала три недели назад, теперь полностью сломана. Но когда именно это произошло? Кто это сломал? В каком коммите появился баг?

Ручной поиск — это кошмар. Переключаться между веток, проверять каждый коммит… это займёт часы. Тут на помощь приходит git bisect — инструмент для бинарного поиска по истории коммитов.

Звучит сложно? На самом деле это просто волшебство! С помощью бинарного поиска можно найти “плохой” коммит среди 1000 коммитов за ~10 проверок. Вместо того чтобы проверять коммиты подряд (O(n)), git bisect использует алгоритм O(log n). Это экспоненциально быстрее!

Как работает бинарный поиск в git #

Принцип простой — разделяй и властвуй:

  1. Git находит вашего “хорошего” коммита (где баг не было)
  2. Git находит “плохого” коммита (где баг точно есть)
  3. Git переключается на коммит ровно посередине между ними
  4. Вы проверяете — есть баг или нет?
  5. Git исключает половину коммитов и повторяет процесс

Вот визуально:

Хороший коммит (v1.0.0) ──────────────┼────────────── Плохой коммит (HEAD)
                                       ↓
                        Проверим коммит посередине

Если баг здесь? ───→ исключаем левую половину
Если нет? ─────────→ исключаем правую половину

Повторяем, пока не найдём точный коммит!

Это работает так же, как поиск слова в словаре: вы не читаете каждую страницу подряд, вы открываете середину, смотрите нужна ли левая или правая половина, и повторяете.

Базовый процесс git bisect #

Давайте разберём пошагово. Вот классический workflow:

# 1. Начинаем сессию bisect
git bisect start

# 2. Указываем плохой коммит (где баг точно есть)
# Обычно это текущая ветка (HEAD)
git bisect bad HEAD

# 3. Указываем хороший коммит (где бага не было)
# Это может быть тег, другая ветка или hash конкретного коммита
git bisect good v1.0.0

# Git автоматически переключится на коммит посередине
# Вы теперь в "detached HEAD" состоянии
# Проверьте, есть ли баг в этом коммите

# 4. Если баг есть, помечаем плохо
git bisect bad

# Если бага нет, помечаем хорошо
git bisect good

# Git переключится на следующий коммит для проверки
# Повторяем шаги 4-5 пока Git не найдёт точный коммит

# 5. Когда git найдёт коммит, он покажет его
# Выглядит так:
# 1a2b3c4d5e6f7g is the first bad commit

# 6. Завершаем сессию bisect
git bisect reset

Практический пример: находим сломанный тест #

Представим реальный сценарий. У нас есть проект, тесты падают, но мы не знаем с какого момента. Историю коммитов можно посмотреть с помощью /ru/git/git-log/.

# Сначала убедимся, что нынешняя ветка имеет падающий тест
npm test
# ✗ FAIL: test/auth.test.js - Login should work
# ✗ 1 test failed

# Начинаем бинарный поиск
git bisect start

# Текущая ветка (HEAD) точно плохая
git bisect bad HEAD

# Известно, что три недели назад всё работало (v2.1.0 - это был релиз)
git bisect good v2.1.0

# Автоматически Git переключилась на коммит посередине
# Вы видите: "Bisecting: 147 revisions left to test after this (roughly 8 steps)"

# Проверяем тесты на этом коммите
npm test
# ✓ PASS: всё работает!

# Баг не здесь, значит он во второй половине
git bisect good

# Git снова переключилась на середину оставшихся коммитов
npm test
# ✗ FAIL: тест падает

# Баг уже был в этом коммите!
git bisect bad

# Продолжаем...
npm test  # ✗ FAIL
git bisect bad

npm test  # ✓ PASS
git bisect good

npm test  # ✗ FAIL
git bisect bad

# После нескольких итераций Git нас уведомляет:
# 3a7d8e9f0a1b2c3d4e5f6g is the first bad commit
# commit 3a7d8e9f0a1b2c3d4e5f6g
# Author: John Doe <[email protected]>
# Date:   Fri Dec 1 14:22:33 2024 +0200
#
#     Fix auth service initialization
#
#     - Changed async flow in AuthService
#     - Removed legacy validation check

# Вот оно! Именно этот коммит сломал логин
git show 3a7d8e9f0a1b2c3d4e5f6g

# Можно даже сразу отменить этот коммит если нужно
# git revert 3a7d8e9f0a1b2c3d4e5f6g

# Завершаем сессию bisect и возвращаемся на исходную ветку
git bisect reset

Видите? Вместо проверки ~300 коммитов подряд, мы нашли проблему за ~8 проверок!

Автоматический режим: git bisect run #

Если у вас есть скрипт, который проверяет наличие бага, можно автоматизировать весь процесс. Git будет сам запускать скрипт и определять good/bad на основе кода выхода.

# Подготовка: создаём скрипт, который возвращает:
# - 0 (успех) если баг НЕ найден (good)
# - любое другое число если баг найден (bad)
# - 125 если нельзя протестировать этот коммит (skip)

# Начинаем bisect как обычно
git bisect start
git bisect bad HEAD
git bisect good v2.1.0

# Теперь Git будет сам запускать команду и проверять результат
# Самый простой способ — использовать встроенные команды
git bisect run npm test

# Или для Python проектов:
git bisect run python -m pytest tests/test_auth.py::test_login

# Или сложнее — комбинируем несколько команд:
git bisect run bash -c "npm run build && npm test"

# Для JavaScript с более сложной логикой:
git bisect run bash -c "npm test 2>&1 | grep -q 'test failed' && exit 1 || exit 0"

# Когда найдёт, покажет результат и вы всё ещё в сессии bisect
# Вы можете выполнить дополнительные проверки
git show HEAD

# Завершаем
git bisect reset

Как это работает? Git выполняет вашу команду для каждого коммита и смотрит на код выхода:

  • Код 0 (успех) = коммит хороший (good)
  • Код 1-124 или >125 = коммит плохой (bad)
  • Код 125 = пропустить этот коммит (skip) — полезно если коммит не компилируется

Вот более умный пример с обработкой ошибок:

# Создаём файл check-bug.sh
cat > check-bug.sh << 'EOF'
#!/bin/bash

# Пробуем собрать проект
npm run build || exit 125  # 125 = skip этот коммит

# Пробуем запустить тесты
if npm test; then
  exit 0  # good - тесты прошли
else
  exit 1  # bad - тесты не прошли
fi
EOF

chmod +x check-bug.sh

# Запускаем bisect с нашим скриптом
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run ./check-bug.sh

# Git автоматически переберёт все коммиты и найдёт плохой!

Это мощный способ! Если у вас есть unit-тесты или integration-тесты, вы можете найти баг полностью автоматически, даже в проекте с тысячами коммитов.

Дополнительные команды и опции #

git bisect имеет несколько полезных команд, которые помогают в сложных ситуациях:

git bisect skip #

Бывает, что вы не можете проверить коммит (он не компилируется, падает при запуске, требует deprecated зависимостей). Используйте skip:

# Коммит не компилируется, пропускаем его
git bisect skip

# Или пропускаем несколько коммитов сразу
git bisect skip HEAD~5..HEAD

Git просто проигнорирует этот коммит и выберет соседний.

git bisect log #

Нужно посмотреть историю вашей текущей сессии bisect? Легко:

git bisect log

# Выведет что-то типа:
# git bisect start
# git bisect bad HEAD
# git bisect good v2.1.0
# git bisect bad
# git bisect good
# git bisect bad

git bisect replay #

Хотите повторить сессию bisect ещё раз или поделиться ею с коллегой? Сохраните лог и воспроизведите:

# Сохраняем лог сессии
git bisect log > bisect-log.txt

# На другой машине или позже воспроизводим
git bisect replay bisect-log.txt

git bisect visualize #

Для визуалов есть команда для отображения в gitk:

# Откроет gitk с выделением протестированных коммитов
git bisect visualize

# После первого разветвления можно использовать просто
git bisect vis

Совместно с другими инструментами #

git bisect отлично работает с инструментами /ru/git/git-reflog/ и /ru/git/git-revert/:

# Найти баг с помощью bisect
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run npm test

# Получив результат, можно отменить коммит
git revert <found-bad-commit>

# Или посмотреть историю действий
git reflog

Если нужно разобраться с историей конкретного файла, посмотрите /ru/git/git-log/ с опцией для одного файла.

Советы по эффективному использованию git bisect #

1. Убедитесь в воспроизводимости бага #

Перед началом bisect убедитесь, что вы можете воспроизвести баг на текущей ветке:

# Проверить баг локально
npm test
# или ручная проверка
npm start
# Проверяем фичу — да, сломана!

# Только потом начинаем bisect
git bisect start

2. Используйте достаточно широкий диапазон #

Если вы не уверены, когда появился баг, берите широкий диапазон:

# Плохо: слишком узкий диапазон, может пропустить
git bisect good HEAD~100
git bisect bad HEAD

# Хорошо: более широкий диапазон
git bisect good v2.0.0  # был три месяца назад
git bisect bad HEAD

3. Используйте автоматический режим где можно #

Если есть тесты, используйте git bisect run:

# Намного быстрее чем вручную проверять каждый коммит
git bisect run npm test

4. Попросите помощь у коллег #

Если вы “заблудились” в сессии bisect, можно посмотреть лог и спросить мнение:

git bisect log

# Отправить коллеге или обсудить что дальше

FAQ: Часто задаваемые вопросы #

Что делает git bisect? #

git bisect — это инструмент для поиска коммита, который сломал какой-то функционал. Он использует бинарный поиск (как поиск в отсортированном массиве или в словаре) для быстрого нахождения “первого плохого коммита” среди сотен или тысяч коммитов. Вместо проверки каждого коммита подряд, он переключается на середину диапазона, вы проверяете есть ли баг, и Git исключает половину коммитов. Так за логарифмическое число шагов находится точный коммит.

Как найти в каком коммите появился баг? #

Используйте git bisect:

git bisect start
git bisect bad HEAD          # текущая версия сломана
git bisect good v1.0.0       # старая версия работала
# ... проверяете коммиты пока Git не найдёт плохой

Или автоматически:

git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run npm test      # Git найдёт сам!

Что делать если нельзя проверить коммит? #

Используйте git bisect skip. Это полезно когда:

  • Коммит не компилируется (неполный commit)
  • Требует зависимостей которые больше недоступны
  • Использует deprecated API
git bisect skip
# Git выберет другой коммит

Как вернуться из git bisect? #

Просто выполните:

git bisect reset

Это вернёт вас на ту ветку, которая была активна до начала bisect.

Что значит git bisect bad? #

Это означает “в этом коммите баг уже присутствует”. Вы проверили текущий коммит, нашли баг, и говорите Git’у что это “плохой” коммит. Git теперь знает что баг появился в этом коммите или раньше, и будет искать в левой половине диапазона.

Как автоматизировать git bisect? #

Используйте git bisect run с командой которая возвращает:

  • 0 если бага нет (good)
  • 1 если баг есть (bad)
  • 125 если не можете протестировать (skip)
git bisect run npm test
git bisect run ./check-bug.sh
git bisect run bash -c "npm run build && npm test"

Сколько времени займёт поиск бага с git bisect? #

Это зависит от количества коммитов:

  • 10 коммитов → ~4 проверки
  • 100 коммитов → ~7 проверок
  • 1000 коммитов → ~10 проверок
  • 10000 коммитов → ~14 проверок

Логарифмическая сложность делает это быстрым даже на больших проектах!

Можно ли использовать bisect на рабочей ветке? #

Да, но обычно лучше сделать это на отдельной ветке или когда у вас нет незакоммиченных изменений. Если у вас есть staged или unstaged changes, Git может попросить их закоммитить или stash’ить перед началом bisect.

# Безопаснее сделать на чистой рабочей директории
git status
# On branch main
# nothing to commit, working tree clean

git bisect start

Заключение #

git bisect — это один из самых недооценённых, но мощных инструментов в Git. Вместо того чтобы часами искать баг вручную, вы получаете автоматический бинарный поиск, который находит проблему за минуты.

Главные моменты:

  • Используйте для поиска “первого плохого коммита”
  • Намного быстрее ручного поиска (O(log n) вместо O(n))
  • Может работать полностью автоматически с git bisect run
  • Работает отлично с unit-тестами и скриптами проверки

В следующий раз когда кто-то скажет “кто сломал код?!” — вы сможете ответить с уверенностью. Потому что найдёте ответ за несколько минут! 🚀