Я прочитал несколько статей о том, что git bisect
это круто. Тем не менее, я не носитель языка и не могу понять, почему это круто.
Может ли кто-нибудь продемонстрировать пример кода:
- Как это использовать?
- Это так же, как
svn blame
?
Я прочитал несколько статей о том, что git bisect
это круто. Тем не менее, я не носитель языка и не могу понять, почему это круто.
Может ли кто-нибудь продемонстрировать пример кода:
svn blame
?Ответы:
Идея 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
команды.
git bisect bad <rev> [<rev>...]
чтобы пометить определенные ревизии как плохие (или хорошие с git bisect good <rev> [<rev>...]
). rev
может быть любым идентификатором ревизии, таким как имя ветви, тег, хеш коммита (или уникальный префикс хэша коммита), ...
git bisect reset
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.
test_script
модульным набором тестов и запустите его из отдельного файла, разделив пополам. Когда вы исправите, объедините тест с основным набором тестов.
bisect run
особенно полезным, когда тестирование занимает много времени, и я почти уверен, что тестовая система не сломается. Таким образом, я могу просто оставить его работать в фоновом режиме или в одночасье, если он потребует слишком много ресурсов, не теряя времени на переключение контекста мозга.
$ 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
git bisect good
чтобы перейти к следующему коммиту.
Просто чтобы добавить еще один пункт:
Мы можем указать имя файла или путь к нему git bisect start
в случае, если мы знаем, что ошибка произошла из определенных файлов. Например, предположим, что мы знали, что изменения, вызвавшие регрессию, были в каталоге com / workingDir, после чего мы можем выполнить. git bisect start com/workingDir
Это означает, что будут проверяться только те коммиты, которые изменили содержимое этого каталога, и это делает вещи еще быстрее.
Кроме того, если вам сложно определить, хорош ли тот или иной коммит, вы можете запустить его git bisect skip
, и он будет игнорироваться. Поскольку других коммитов достаточно, git bisect будет использовать другой, чтобы сузить поиск.
$ 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
Примечание: термины 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)