git-bisect
Имя
git-bisect — используйте двоичный поиск, чтобы найти коммит, в котором появилась ошибка
Краткое описание
git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
[--no-checkout] [--first-parent] [<bad> [<good>…]] [--] [<pathspec>…]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>…]
git bisect terms [--term-(good|old) | --term-(bad|new)]
git bisect skip [(<rev>|<range>)…]
git bisect next
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd> [<arg>…]
git bisect help Описание
Эта команда использует алгоритм двоичного поиска, чтобы найти коммит в истории проекта, в котором появилась ошибка. Сначала укажите «плохой» коммит, в котором заведомо есть ошибка, и «хороший» коммит, который заведомо предшествует её появлению. Затем git bisect выбирает коммит между этими двумя точками и спрашивает, является ли выбранный коммит «хорошим» или «плохим». Команда продолжает сужать диапазон, пока не найдёт точный коммит, в котором появилось изменение.
На самом деле git bisect можно использовать, чтобы найти коммит, изменивший любое свойство вашего проекта; например, коммит, исправивший ошибку, или коммит, после которого улучшилась производительность бенчмарка. Для поддержки такого более общего применения вместо терминов «хороший» и «плохой» можно использовать термины «старый» и «новый» или выбрать собственные термины. Дополнительную информацию см. в разделе «Другие термины» ниже.
Основные команды bisect: start, bad, good
Предположим, что вы пытаетесь найти коммит, в котором перестала работать функция, работавшая в версии v2.6.13-rc2 вашего проекта. Начните сеанс bisect следующим образом:
$ git bisect start $ git bisect bad # Current version is bad $ git bisect good v2.6.13-rc2 # v2.6.13-rc2 is known to be good
После того как вы укажете хотя бы один плохой и один хороший коммит, git bisect выберет коммит из середины этого диапазона истории, переключится на него и выведет примерно следующее:
Bisecting: 675 revisions left to test after this (roughly 10 steps)
Теперь следует скомпилировать выбранную версию и проверить её. Если она работает правильно, введите
$ git bisect good
Если эта версия не работает, введите
$ git bisect bad
После этого git bisect ответит примерно так:
Bisecting: 337 revisions left to test after this (roughly 9 steps)
Продолжайте повторять эту процедуру: собирайте дерево, проверяйте его и в зависимости от того, является ли оно хорошим или плохим, выполняйте git bisect good или git bisect bad, чтобы запросить следующий коммит для проверки.
В конце концов не останется ревизий для проверки, и команда выведет описание первого плохого коммита. Ссылка refs/bisect/bad будет указывать на этот коммит.
Сброс bisect
После сеанса bisect, чтобы очистить состояние бисекции и вернуться к исходному HEAD, выполните следующую команду:
git bisect reset
По умолчанию эта команда вернёт ваше дерево к коммиту, на который был выполнен checkout до git bisect start. (Новая команда git bisect start также сделает это, поскольку очищает состояние предыдущей бисекции.)
С помощью необязательного аргумента можно вместо этого вернуться к другому коммиту:
git bisect reset <commit>
Например, git bisect reset bisect/bad переключится на первую плохую ревизию, тогда как git bisect reset HEAD оставит вас на текущем коммите бисекции и вообще не будет переключать коммиты.
Другие термины
Иногда вам нужно найти не коммит, в котором появилось нарушение, а коммит, вызвавший изменение между некоторым другим «старым» и «новым» состояниями. Например, вы можете искать коммит, в котором появилось определённое исправление. Или первый коммит, в котором имена всех файлов исходного кода наконец были приведены к принятому в вашей компании стандарту именования. Или что-нибудь ещё.
В таких случаях термины «хороший» и «плохой» могут сбивать с толку, если ими обозначать «состояние до изменения» и «состояние после изменения». Поэтому вместо терминов «хороший» и «плохой» можно использовать соответственно термины «старый» и «новый». (Однако обратите внимание: в одном сеансе нельзя смешивать термины «хороший» и «плохой» с терминами «старый» и «новый».)
При таком более общем использовании укажите коммит «нового» состояния, обладающий некоторым свойством, и коммит «старого» состояния, у которого этого свойства нет, с помощью git bisect. Каждый раз, когда git bisect переключается на коммит, проверяйте, обладает ли он этим свойством. Если обладает, пометьте коммит как «новый»; в противном случае — как «старый». По завершении бисекции git bisect сообщит, в каком коммите появилось это свойство.
Чтобы использовать «старый» и «новый» вместо «хорошего» и «плохого», выполните git bisect start без указания коммитов в качестве аргументов, а затем выполните следующие команды, чтобы добавить коммиты:
git bisect old [<rev>]
чтобы указать, что коммит предшествовал искомому изменению, или
git bisect new [<rev>…]
чтобы указать, что он последовал за ним.
Чтобы напомнить себе, какие термины используются в данный момент, выполните
git bisect terms
Получить только старый термин можно с помощью git bisect terms --term-old или git bisect terms --term-good; git bisect terms --term-new и git bisect terms --term-bad помогут узнать, как обозначать коммиты, более новые, чем искомое изменение.
Если вместо «плохого»/«хорошего» или «нового»/«старого» вы хотите использовать собственные термины, можно выбрать любые названия (кроме существующих подкоманд bisect, таких как reset, start, …), начав бисекцию с помощью
git bisect start --term-old <term-old> --term-new <term-new>
Например, если вы ищете коммит, в котором ухудшилась производительность, можно использовать
$ git bisect start --term-old fast --term-new slow
А если вы ищете коммит, в котором была исправлена ошибка, можно использовать
$ git bisect start --term-new fixed --term-old broken
Затем используйте git bisect <term-old> и git bisect <term-new> вместо git bisect good и git bisect bad для пометки коммитов.
Визуализация/просмотр bisect
Чтобы просмотреть оставшиеся подозрительные коммиты в gitk, выполните во время бисекции следующую команду (вместо подкоманды visualize можно использовать view):
$ git bisect visualize
Git определяет наличие графической среды с помощью различных переменных окружения:
-
DISPLAY -
устанавливается в средах X Window System в системах Unix.
-
SESSIONNAME -
устанавливается в Cygwin в интерактивных сеансах рабочего стола.
-
MSYSTEM -
устанавливается в Msys2 и Git for Windows.
-
SECURITYSESSIONID -
может устанавливаться в macOS в интерактивных сеансах рабочего стола.
Если ни одна из этих переменных окружения не установлена, вместо неё используется git log. Можно также передать параметры командной строки, например -p и --stat.
$ git bisect visualize --stat
Журнал bisect и повторное воспроизведение bisect
После того как вы пометили ревизии как хорошие или плохие, выполните следующую команду, чтобы просмотреть уже выполненные действия:
$ git bisect log
Если вы обнаружили, что неверно указали состояние ревизии, сохраните вывод этой команды в файл, отредактируйте его, удалив ошибочные записи, а затем выполните следующие команды, чтобы восстановить правильное состояние:
$ git bisect reset $ git bisect replay that-file
Пропуск проверки коммита
Если в ходе сеанса bisect вы знаете, что предложенная ревизия не подходит для проверки (например, она не собирается, и вы знаете, что эта ошибка не связана с искомой проблемой), можно вручную выбрать соседний коммит и проверить его вместо неё.
Например:
$ git bisect good/bad # previous round was good or bad.
Bisecting: 337 revisions left to test after this (roughly 9 steps)
$ git bisect visualize # oops, that is uninteresting.
$ git reset --hard HEAD~3 # try 3 revisions before what
# was suggested Затем соберите и проверьте выбранную ревизию, после чего обычным способом пометьте её как хорошую или плохую.
Пропуск bisect
Вместо того чтобы самостоятельно выбирать соседний коммит, можно поручить это Git, выполнив команду:
$ git bisect skip # Current version cannot be tested
Однако если пропустить коммит, соседний с искомым, Git не сможет точно определить, какой из этих коммитов был первым плохим.
Можно пропустить диапазон коммитов, а не только один коммит, используя обозначение диапазона. Например:
$ git bisect skip v2.5..v2.6
Это указывает процессу бисекции, что не следует проверять коммиты после v2.5 и вплоть до v2.6 включительно.
Обратите внимание: если нужно также пропустить первый коммит диапазона, выполните команду:
$ git bisect skip v2.5 v2.5..v2.6
Это указывает процессу бисекции пропустить коммиты между v2.5 и v2.6 включительно.
Следующий шаг bisect
Обычно после пометки ревизии как хорошей или плохой Git автоматически вычисляет следующую ревизию для проверки и переключается на неё. Однако если нужно явно запросить следующий шаг бисекции, используйте:
$ git bisect next
Эта команда может пригодиться, чтобы продолжить процесс бисекции после прерывания, вызванного переключением на другую ревизию.
Сокращение бисекции с помощью дополнительных параметров bisect start
Если вы знаете, какая часть дерева связана с исследуемой проблемой, можно ещё больше сократить число проверок, указав параметры pathspec при выполнении команды bisect start:
$ git bisect start -- arch/i386 include/asm-i386
Если заранее известно несколько хороших коммитов, можно сузить диапазон бисекции, указав все хорошие коммиты сразу после плохого коммита при выполнении команды bisect start:
$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
# v2.6.20-rc6 is bad
# v2.6.20-rc4 and v2.6.20-rc1 are good Запуск bisect
Если у вас есть скрипт, способный определить, является ли текущий исходный код хорошим или плохим, можно выполнить бисекцию с помощью команды:
git bisect run <cmd> [<arg>…]
Обратите внимание: при запуске с помощью <arg> команда <cmd> должна завершаться с кодом 0, если текущий исходный код хороший/старый, и с кодом от 1 до 127 включительно (кроме 125), если текущий исходный код плохой/новый.
Любой другой код завершения прервёт процесс бисекции. Следует учитывать, что программа, завершающая работу через exit(-1), оставляет значение $? = 255 (см. справочную страницу exit(3)), поскольку значение обрезается с помощью & 0377.
Специальный код завершения 125 следует использовать, когда текущий исходный код невозможно проверить. Если скрипт завершится с этим кодом, текущая ревизия будет пропущена (см. раздел git bisect skip выше). Значение 125 выбрано как наибольшее разумное для этой цели, поскольку оболочки POSIX используют значения 126 и 127 для обозначения определённых ошибок (127 означает, что команда не найдена, 126 — команда найдена, но не может быть запущена; эти подробности не имеют значения, поскольку для bisect run это обычные ошибки в скрипте).
Во время сеанса bisect вам может потребоваться внести временные изменения (например, s/#define DEBUG 0/#define DEBUG 1/ в файл заголовка или применить «патч к ревизии, в которой отсутствует этот коммит, поскольку он нужен для обхода другой проблемы, не относящейся к текущей бисекции») к проверяемой ревизии.
Чтобы справиться с такой ситуацией, после того как внутренний вызов git bisect найдёт следующую ревизию для проверки, скрипт может применить патч перед сборкой, запустить настоящую проверку, затем определить, прошла ли ревизия проверку (возможно, с необходимым патчем), и после этого вернуть дерево в исходное состояние. Наконец, скрипт должен завершиться с кодом завершения настоящей проверки, чтобы цикл команды git bisect run мог определить итог сеанса бисекции.
Параметры
-
--no-checkout -
Не выполняйте checkout нового рабочего дерева на каждой итерации процесса бисекции. Вместо этого просто обновляйте ссылку с именем
BISECT_HEAD, чтобы она указывала на коммит, который нужно проверить.Этот параметр может быть полезен, если для проверки на каждом шаге не требуется рабочее дерево с выполненным checkout.
Если репозиторий является голым, предполагается наличие параметра
--no-checkout. -
--first-parent -
При обнаружении коммита слияния следуйте только по первому родительскому коммиту.
При обнаружении регрессий, появившихся в результате слияния ветки, коммит слияния будет определён как коммит, в котором появилась ошибка, а его предки будут проигнорированы.
Этот параметр особенно полезен для предотвращения ложных срабатываний, когда слитая ветка содержала ошибочные коммиты или коммиты, которые нельзя собрать, но само слияние было корректным.
Примеры
-
Автоматически выполните бисекцию, чтобы найти коммит, нарушивший сборку, между v1.2 и
HEAD:$ git bisect start HEAD v1.2 -- # HEAD is bad, v1.2 is good $ git bisect run make # "make" builds the app $ git bisect reset # quit the bisect session
-
Автоматически выполните бисекцию, чтобы найти коммит, вызвавший сбой теста, между origin и
HEAD:$ git bisect start HEAD origin -- # HEAD is bad, origin is good $ git bisect run make test # "make test" builds and tests $ git bisect reset # quit the bisect session
-
Автоматически выполните бисекцию, чтобы найти коммит, нарушивший тест:
$ cat ~/test.sh #!/bin/sh make || exit 125 # this skips broken builds ~/check_test_case.sh # does the test case pass? $ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 $ git bisect run ~/test.sh $ git bisect reset # quit the bisect session
Здесь используется пользовательский скрипт
test.sh. В этом скрипте, еслиmakeзавершается с ошибкой, текущий коммит пропускается. Если тест проходит, командаcheck_test_case.shдолжна завершиться с кодомexit0, а в противном случае — с кодомexit1.Безопаснее, если и
test.sh, иcheck_test_case.shнаходятся за пределами репозитория, чтобы избежать взаимодействия между процессами bisect,makeи тестирования, а также скриптами. -
Автоматически выполните бисекцию с временными изменениями (горячим исправлением):
$ cat ~/test.sh #!/bin/sh # tweak the working tree by merging the hot-fix branch # and then attempt a build if git merge --no-commit --no-ff hot-fix && make then # run project specific test and report its status ~/check_test_case.sh status=$? else # tell the caller this is untestable status=125 fi # undo the tweak to allow clean flipping to the next commit git reset --hard # return control exit $statusПеред каждым запуском теста применяются изменения из ветки с горячим исправлением. Это может пригодиться, например, если в вашей среде сборки или тестирования произошли изменения и для старых ревизий может понадобиться исправление, уже включённое в новые. (Убедитесь, что ветка с горячим исправлением основана на коммите, присутствующем во всех проверяемых ревизиях, чтобы слияние не подтянуло лишнее, или используйте
gitcherry-pickвместоgitmerge.) -
Автоматически выполните бисекцию, чтобы найти коммит, нарушивший тест:
$ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh" $ git bisect reset # quit the bisect session
Этот пример показывает, что можно обойтись без скрипта запуска, если записать тест в одну строку.
-
Найдите исправную область графа объектов в повреждённом репозитории
$ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout $ git bisect run sh -c ' GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) && git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ && git pack-objects --stdout >/dev/null <tmp.$$ rc=$? rm -f tmp.$$ test $rc = 0' $ git bisect reset # quit the bisect sessionВ этом случае после завершения
gitbisectrunссылкаbisect/badбудет указывать на коммит, у которого есть хотя бы один родитель с полностью обходимым графом достижимых объектов в смысле, необходимом дляgitpack-objects. -
Ищите исправление, а не регрессию в коде
$ git bisect start $ git bisect new HEAD # current commit is marked as new $ git bisect old HEAD~10 # the tenth commit from now is marked as old
или:
$ git bisect start --term-old broken --term-new fixed $ git bisect fixed $ git bisect broken HEAD~10
Получение справки
Используйте git bisect, чтобы получить краткое описание использования, и git bisect help или git bisect -h, чтобы получить подробное описание использования.
См. также
bisect
© 2005–2026 Linus Torvalds and others
Licensed under the GNU General Public License version 2.
https://git-scm.com/docs/git-bisect