git bisect: поиск бага в истории коммитов
Что такое git bisect и зачем он нужен #
Представьте ситуацию: вы разрабатываете проект с историей в 500+ коммитов, и вдруг выясняется, что фича, которая работала три недели назад, теперь полностью сломана. Но когда именно это произошло? Кто это сломал? В каком коммите появился баг?
Ручной поиск — это кошмар. Переключаться между веток, проверять каждый коммит… это займёт часы. Тут на помощь приходит git bisect — инструмент для бинарного поиска по истории коммитов.
Звучит сложно? На самом деле это просто волшебство! С помощью бинарного поиска можно найти “плохой” коммит среди 1000 коммитов за ~10 проверок. Вместо того чтобы проверять коммиты подряд (O(n)), git bisect использует алгоритм O(log n). Это экспоненциально быстрее!
Как работает бинарный поиск в git #
Принцип простой — разделяй и властвуй:
- Git находит вашего “хорошего” коммита (где баг не было)
- Git находит “плохого” коммита (где баг точно есть)
- Git переключается на коммит ровно посередине между ними
- Вы проверяете — есть баг или нет?
- 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-тестами и скриптами проверки
В следующий раз когда кто-то скажет “кто сломал код?!” — вы сможете ответить с уверенностью. Потому что найдёте ответ за несколько минут! 🚀