Как использовать git bisect?

Я прочитал несколько статей о том, что git bisect - это круто. Однако я не являюсь носителем языка и не могу понять, почему это здорово.

Может кто-нибудь продемонстрировать пример кода:

  1. Как это использовать?
  2. Это так же, как svn blame?

Ответ 1

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

... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current

Вы знаете, что ваша программа не работает должным образом в версии current и что она работает в редакции 0. Таким образом, регрессия, вероятно, была введена в одном из коммитов 1, 2, 3, 4, 5, current.

Вы можете попытаться проверить каждую фиксацию, построить ее, проверить, присутствует ли регрессия или нет. Если есть большое количество коммитов, это может занять много времени. Это линейный поиск. Мы можем сделать лучше, выполнив двоичный поиск. Это то, что делает команда git bisect. На каждом шаге он пытается уменьшить количество исправлений, которые потенциально плохи в два раза.

Вы будете использовать команду следующим образом:

$ git stash save
$ git bisect start
$ git bisect bad
$ git bisect good 0
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[< ... sha ... >] 3

После этой команды git проверит фиксацию. В нашем случае это будет commit 3. Вам нужно создать свою программу и проверить, присутствует ли регресс. Вам также нужно сообщить git статус этой версии с помощью git bisect bad, если присутствует регрессия, или git bisect good, если это не так.

Предположим, что регрессия была введена в commit 4. Тогда регрессия отсутствует в этой ревизии, и мы говорим ей git.

$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5

Затем он выберет другую фиксацию. Либо 4, либо 5 (так как есть только две коммиты). Предположим, что он выбрал 5. После сборки мы тестируем программу и видим, что существует регрессия. Затем мы расскажем об этом git:

$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4

Мы тестируем последнюю ревизию, 4. И поскольку это та, которая вводила регрессию, мы говорим ей git:

$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >

В этой простой ситуации нам пришлось тестировать только 3 версии (3, 4, 5) вместо 4 (1, 2, 3, 4). Это небольшая победа, но это потому, что наша история настолько мала. Если диапазон поиска составляет N коммитов, мы должны ожидать, чтобы тест 1 + log2 N совершил с git bisect, а не примерно N/2 совершил линейный поиск.

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

Ответ 2

git bisect run автоматическое разделение

Если у вас есть автоматический скрипт ./test, который имеет статус выхода 0, если тест в порядке, вы можете автоматически найти ошибку с помощью bisect run:

git checkout KNOWN_BAD_COMMIT
git bisect start

# Confirm that our test script is correct, and fails on the bad commit.
./test
# Should output != 0.
echo $?
# Tell Git that the current commit is bad.
git bisect bad

# Same for a known good commit in the past.
git checkout KNOWN_GOOD_COMMIT
./test
# Should output 0.
echo $?
# After this, git automatically checks out to the commit
# in the middle of KNOWN_BAD_COMMIT and KNOWN_GOOD_COMMIT.
git bisect good

# Bisect automatically all the way to the first bad or last good rev.
git bisect run ./test

# End the bisect operation and checkout to master again.
git bisect reset

Это, конечно, предполагает, что если тестовый скрипт ./test отслеживается в git, то он не исчезает при более раннем коммите во время деления пополам.

Я обнаружил, что очень часто вы можете уйти, просто скопировав скрипт из дерева из дерева, и, возможно, поиграв с переменными PATH -like, и запустив его оттуда.

Конечно, если тестовая инфраструктура, от которой зависит test, прерывается от старых коммитов, то решения не существует, и вам придется делать что-то вручную, решая, как тестировать коммиты один за другим.

Дополнительные советы

Оставайтесь на первом неудачном коммите после деления пополам, а не возвращайтесь к master:

git bisect reset HEAD

start + начальные bad и good за один раз:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

- это то же самое, что и

git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT

Посмотрите, что было проверено до сих пор (руководство good и bad или run):

git bisect log

Пример вывода:

git bisect log
git bisect start
# bad: [00b9fcdbe7e7d2579f212b51342f4d605e53253d] 9
git bisect bad 00b9fcdbe7e7d2579f212b51342f4d605e53253d
# good: [db7ec3d602db2d994fe981c0da55b7b85ca62566] 0
git bisect good db7ec3d602db2d994fe981c0da55b7b85ca62566
# good: [2461cd8ce8d3d1367ddb036c8f715c7b896397a5] 4
git bisect good 2461cd8ce8d3d1367ddb036c8f715c7b896397a5
# good: [8fbab5a3b44fd469a2da3830dac5c4c1358a87a0] 6
git bisect good 8fbab5a3b44fd469a2da3830dac5c4c1358a87a0
# bad: [dd2c05e71c246f9bcbd2fbe81deabf826c54be23] 8
git bisect bad dd2c05e71c246f9bcbd2fbe81deabf826c54be23
# bad: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05] 7
git bisect bad c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05
# first bad commit: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c0

Покажите хорошие и плохие ссылки на git log, чтобы лучше понять время:

git log --decorate --pretty=fuller --simplify-by-decoration master

Это показывает только коммиты с соответствующей ссылкой, которая снижает уровень шума, но включает автоматически сгенерированные ссылки типа:

refs/bisect/good*
refs/bisect/bad*

который говорит нам, какие коммиты мы пометили как хорошие или плохие.

Рассмотрите этот тестовый репозиторий, если вы хотите поиграть с командой.

Отказ быстр, успех медленный

Иногда:

  • сбой происходит быстро, например один из первых тестовых перерывов
  • успех занимает некоторое время, например пройденный тест пройден, и все остальные тесты, о которых мы не заботимся, следуют

Для таких случаев, например, Предположим, что сбой всегда происходит в течение 5 секунд, и если нам лень сделать тест более конкретным, чем нужно, мы можем использовать timeout как в:

#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
  exit 1
fi

Это работает, поскольку timeout выходит из 124, а сбой test-command выходит из 1.

Статусы магического выхода

git bisect run немного требователен к статусам выхода:

  • все, что выше 127, приводит к сбою деления пополам с чем-то вроде:

    git bisect run failed:
    exit code 134 from '../test -aa' is < 0 or >= 128
    

    В частности, C assert(0) приводит к SIGABRT и выходит со статусом 134, что очень раздражает.

  • 125 - это магия, и бег с ней пропускается git bisect skip.

    Цель этого состоит в том, чтобы помочь пропускать неработающие сборки по несвязанным причинам.

Подробнее см. в man git-bisect.

Так что вы можете использовать что-то вроде:

#!/usr/bin/env bash
set -eu
./build
status=0
./actual-test-command || status=$?
if [ "$status" -eq 125 ] || [ "$status" -gt 127 ]; then
  status=1
fi
exit "$status"

Проверено на git 2.16.1.

Ответ 3

TL; DR

Начало

$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>

Bisecting: X revisions left to test after this (roughly Y steps)

Повтор:

Проблема все еще существует?

  • Да: $ git bisect bad
  • Нет: $ git bisect good

Результат:

<abcdef> is the first bad commit

Когда выполнено:

git bisect reset

Ответ 4

Просто добавьте еще один пункт:

Мы можем указать имя файла или путь к git bisect start, если мы знаем, что ошибка возникла из определенных файлов. Например, Предположим, мы знали, что изменения, вызвавшие регресс, были в com/workingDir то мы можем запустить git bisect start com/workingDir Это означает, что будут проверены только те коммиты, которые изменили содержимое этого каталога, и это делает вещи еще быстрее.

Кроме того, если его трудно определить, является ли конкретная фиксация хорошей или плохой, вы может запустить git bisect skip, что будет игнорировать его. Учитывая, что есть достаточно других commits, git bisect будет использовать другой, чтобы сузить поиск.

Ответ 5

$ git bisect .. базовый инструмент Git для отладки. 'Git Bisect', пройдя предыдущие совершает с момента последнего (известного) рабочего коммита. Он использует бинарный поиск, чтобы пройти через все эти коммиты, чтобы добраться до того, который ввел регрессию/ошибку.

$ git bisect start # Запуск bisect

$ git bisect bad #, заявив, что текущая фиксация (v1.5) имеет регрессию/установку "плохая" точка

$ git bisect good v1.0 #, отметив его последним хорошим рабочим фиксацией (без регрессии)

Это упоминание "плохих" и "хороших" точек поможет git bisect (двоичный поиск) выбрать средний элемент (commit v1.3). Если регрессия существует при совершении v1.3, вы установите ее как новую "плохую" точку, то есть (Good → v1.0 и Bad → v1.3)

$ git bisect bad

или аналогично, если commit v1.3 без ошибок, вы установите его как новую "Хорошую точку", то есть (* Good → v1.3 и Bad → v1.6).

$ git bisect good

Ответ 6

Примечание: термины good и bad - не единственные, которые можно использовать для обозначения коммита с определенным свойством или без него.

Git 2.7 (четвертый квартал 2015 года) представил новые опции git bisect.

 git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
                  [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]

С добавлением документации:

Иногда вы ищете не коммит, который привел к поломке, а скорее коммит, который вызвал изменение между каким-то другим "старым" состоянием и "новым" состоянием.

Например, вы можете искать коммит, который представил конкретное исправление.
Или вы можете искать первый коммит, в котором имена файлов исходного кода были окончательно преобразованы в стандарт именования вашей компании. Или что угодно.

В таких случаях может быть очень сложно использовать термины "хорошо" и "плохо" для обозначения "состояние до изменения" и "состояние после изменения".

Таким образом, вместо " good " и " bad " вы можете использовать термины " old " и " new " соответственно.
(Но учтите, что нельзя смешивать " good " и " bad " со " old " и " new " в одном сеансе.)

В этом более общем использовании вы предоставляете git bisect " new " коммит с некоторым свойством и " old " коммит, у которого этого свойства нет.

Каждый раз, когда git bisect проверяет коммит, вы проверяете, имеет ли этот коммит свойство:
Если это так, пометьте коммит как " new "; в противном случае отметьте его как " old ".

Когда деление будет выполнено, git bisect сообщит, какой коммит ввел свойство.


См. Коммит 06e6a74, коммит 21b55e3, коммит fe67687 (29 июня 2015 г.) от Matthieu Moy (moy).
См. Коммит 21e5cfd (29 июня 2015 г.) Антуана Делайта (CanardChouChinois).
(Объединено Junio C Hamano - gitster - в коммите 22dd6eb, 05 октября 2015 г.)