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


439

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

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

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

@ 01: Как говорит git book: выполнить поиск методом «грубой силы» по истории проекта .
Eckes

7
Не так /// brute :-), он использует бинарный поиск.
Кожа

"git blame" похож на "svn blame". "git bisect" - это совсем другое
Уильям Перселл

Для чего это стоит, есть хорошее описание bisect в Pro Git тоже. Ответ Сильвейна - еще один хороший выстрел в этом. Если, посмотрев на все это, вы все равно не получите его, я бы посоветовал вам задать более конкретный вопрос. Общие вопросы порождают общие ответы.
Каскабель

4
обновлена ​​ссылка на книгу: git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search
gdelfino

Ответы:


656

Идея 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будет оформлен коммит. В нашем случае это будет коммит 3. Вам нужно собрать свою программу и проверить, присутствует ли регрессия. Вам также нужно будет сообщить gitстатус этой ревизии либо, git bisect badесли регрессия присутствует, либо git bisect goodее нет.

Давайте предположим, что регрессия была введена в коммите 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команды.


5
Я собираюсь заключить контракт здесь, это хорошее объяснение пополам, но на самом деле не помогает мне его использовать. Тем более, что мне удалось найти хороший коммит и я сейчас в этой ветке. С этой позиции это объяснение ничем не поможет. Как указать плохую ветку, не проверяя ее, например
PandaWood

4
Вы можете использовать, git bisect bad <rev> [<rev>...]чтобы пометить определенные ревизии как плохие (или хорошие с git bisect good <rev> [<rev>...]). revможет быть любым идентификатором ревизии, таким как имя ветви, тег, хеш коммита (или уникальный префикс хэша коммита), ...
Sylvain Defresne

39
... и как только вы закончите, вы git bisect reset
печатаете,

17
Один из самых крутых ответов в стеке. Очень хорошо сформулированы. Я делал этот процесс вручную в течение многих лет, просто выбирая произвольную промежуточную точку между хорошим и плохим коммитом, затем снова между этим и хорошим / плохим, в зависимости от того, был ли он хорошим / плохим. Это всегда было огромной болью в заднице, и я никогда даже не слышал об этой подкоманде git до сегодняшнего дня ... хахаха
Chev

3
@ Nemoden, да, может, в принципе это может быть полезно для любого типа проекта. Вам просто нужно заменить шаг «сделать тест» на «развернуть веб-сайт и воспроизвести проблему»
alex.b

159

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переменными, подобными переменным, и запустив его вместо этого.

Конечно, если тестовая инфраструктура, от которой 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)ведет к a 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.


7
Откуда git знает, чтобы ваш новый тест не исчезал при возврате / делении пополам на предыдущую / плохую ревизию (у которой не было вашего вновь написанного теста)?
thebjorn

8
@thebjorn у вас есть точка зрения: насколько я знаю, либо тест должен быть на внешнем исполняемом файле в PATH, либо в неотслеживаемом файле в репо. Во многих случаях это возможно: поместите тест в отдельный файл, включите необходимый шаблон теста с хорошо разработанным test_scriptмодульным набором тестов и запустите его из отдельного файла, разделив пополам. Когда вы исправите, объедините тест с основным набором тестов.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 六四 事件 法轮功 纳米比亚 威 视 Извините, я откатил вашу правку ниже, потому что я чувствую, что она более понятна для новичков и просто добавляет еще один пункт к вашему ответу (не точный ответ, как ваш - поэтому упомяну это, его добавить точку)
Никс

1
Есть много способов ошибиться с «git bisect run» - например, посмотреть, как хороший коммит был отменен неудачным слиянием. Он входит, выходит, снова и снова, и только последний «выход» - это плохо. Тем не менее, вы всегда можете сделать вручную «git bisect». Поскольку это биссектриса, требуется всего несколько шагов - например, 1024 коммитов за 10 шагов.
комбинатор

1
@combinatorist вы правы, что это может потерпеть неудачу. Я считаю bisect runособенно полезным, когда тестирование занимает много времени, и я почти уверен, что тестовая система не сломается. Таким образом, я могу просто оставить его работать в фоновом режиме или в одночасье, если он потребует слишком много ресурсов, не теряя времени на переключение контекста мозга.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

124

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

3
Убедитесь, что вы находитесь в корне вашего git-репо, или вы получите странное «Вам нужно запустить эту команду с верхнего уровня рабочего дерева». ошибка.
PR Whitehead

Так что я сделал git bad на моем HEAD и git good на первом коммите, когда ошибки не было. Так что же делать дальше? когда ошибки нет, git bisect хорошо, чтобы перейти к следующему коммиту?
Гоблины

@Gobliins Если ошибки нет, исправьте, git bisect goodчтобы перейти к следующему коммиту.
Джеффри Хейл

40

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

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

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


15

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

$ git bisect start # Начальный биссект

$ 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

или аналогично, если коммит v1.3 не содержит ошибок, вы установите его в качестве новой «Хорошей точки», т. е. (* Good -> v1.3 и Bad -> v1.6).

$ git bisect good

2

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

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

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

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

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

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

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

Таким образом , вместо этого, вы можете использовать термины « old» и « new», соответственно, вместо « good» и « bad».
(Но учтите, что вы не можете смешивать " 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 , 5 октября 2015)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.