Spec-Zone.ru › Git

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 должна завершиться с кодом exit 0, а в противном случае — с кодом exit 1.

    Безопаснее, если и 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

    Перед каждым запуском теста применяются изменения из ветки с горячим исправлением. Это может пригодиться, например, если в вашей среде сборки или тестирования произошли изменения и для старых ревизий может понадобиться исправление, уже включённое в новые. (Убедитесь, что ветка с горячим исправлением основана на коммите, присутствующем во всех проверяемых ревизиях, чтобы слияние не подтянуло лишнее, или используйте git cherry-pick вместо git merge.)

  • Автоматически выполните бисекцию, чтобы найти коммит, нарушивший тест:

    $ 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

    В этом случае после завершения git bisect run ссылка bisect/bad будет указывать на коммит, у которого есть хотя бы один родитель с полностью обходимым графом достижимых объектов в смысле, необходимом для git pack-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, чтобы получить подробное описание использования.

См. также

Борьба с регрессиями с помощью git bisect, git-blame[1].

bisect

© 2005–2026 Linus Torvalds and others
Licensed under the GNU General Public License version 2.
https://git-scm.com/docs/git-bisect

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API