Я прочитал несколько статей о том, что 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
проверит фиксацию. В нашем случае это будет 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
.
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.
$ 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 start
, если мы знаем, что ошибка возникла из определенных файлов.
Например,
Предположим, мы знали, что изменения, вызвавшие регресс, были в com/workingDir
то мы можем запустить git bisect start com/workingDir
Это означает, что
будут проверены только те коммиты, которые изменили содержимое этого каталога, и
это делает вещи еще быстрее.
Кроме того, если его трудно определить, является ли конкретная фиксация хорошей или плохой, вы
может запустить git bisect skip
, что будет игнорировать его. Учитывая, что есть достаточно других
commits, git bisect будет использовать другой, чтобы сузить поиск.
$ 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
Примечание: термины 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 г.)