Як користуватися git bisect?


438

Я прочитав деякі статті, які говорять, що git bisectце приголомшливо. Однак я не є носієм мови і не можу зрозуміти, чому це приголомшливо.

Може хтось, будь ласка, продемонструє якийсь зразок коду:

  1. Як ним користуватися?
  2. Це просто так svn blame?

@ 01: Як говориться в книзі git: виконайте брутальний пошук через історію проекту .
eckes

7
Не так /// brute :-), він використовує двійковий пошук.
кожокар

"git blame" схожий на "svn blame". "git bisect" - зовсім інша річ
Вільям Перселл

Для чого це варто, є і хороший опис бісектії в Pro Git . Відповідь Сільвейна - це ще один хороший випадок. Якщо після перегляду всього цього ви все ще не зрозумієте, я б запропонував вам задати більш конкретне запитання. Загальні питання породжують загальні відповіді.
Каскабель

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

Відповіді:


655

Ідея 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може бути будь-який ідентифікатор редакції, наприклад, ім'я гілки, тег, хеш-
коміт

39
... і як тільки ви закінчите, ви вводите git bisect resetвсе, щоб повернути все на нещодавнє зобов’язання
peetonn

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відслідковується, він не зникає на деяких попередніх фіксаціях під час поділу.

Я виявив, що дуже часто ви можете піти, просто скопіювавши сценарій дерева з дерева та, можливо, погравши зі 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, щоб краще зрозуміти час:

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.


7
Як git знає, щоб уникнути зникнення вашого нового тесту при поверненні / переході до попереднього / поганого перегляду (у якому не було нещодавно написаного тесту)?
thebjorn

8
@thebjorn у вас є пункт: наскільки я знаю, або тест повинен бути на зовнішньому виконуваному файлі в PATH, або у відслідковуваному файлі репо. У багатьох випадках це можливо: помістіть тест в окремий файл, включіть необхідну тестову табличку з добре складеним test_script+ модульним набором тесту та запустіть її з окремого файлу під час розбиття на бік. Коли ви виправите, об'єднайте тест у основний тестовий набір.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

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

1
Існує багато способів помилитися з "git bisect run" - наприклад, побачити, як хороший фіксатор був відмінений поганим злиттям. Він заходить, виходить, повертається знову і знову, і тільки останнє "вихід" погано. Однак ви завжди можете виконати ручну "біт-біт". Оскільки це бісектриса, вона виконує лише кілька кроків - наприклад, 1024 здійснює за 10 кроків.
комбінаторист

1
@combinatorist ви праві, що це може вийти з ладу. Мені здається bisect runособливо корисним, коли тест потребує тривалого часу, і я майже впевнений, що тестова система не зламається. Таким чином, я можу просто залишити його працювати на задньому плані або на ніч, якщо це забирає занадто багато ресурсів, не втрачаючи часу на перемикання контексту мозку.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

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 repo, або ви отримаєте дивне "Вам потрібно запустити цю команду з вершини робочого дерева". помилка.
PR Whitehead

Так що я зробив поганий gad на моїй голові та 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, ви встановите її як нову "погану" точку, тобто ( Добре -> v1.0 і погано -> v1.3 )

$ git bisect bad

або аналогічно, якщо команда v1.3 відсутня помилок, ви встановите її як нову "Добру точку", тобто (* Добре -> v1.3 і Погано -> v1.6).

$ git bisect good

2

Примітка. Умови goodта терміни badне є єдиними, які ви можете використовувати для позначення комісії з певним властивістю або без неї.

Git 2.7 (Q4 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 р.) Матьє Моя ( moy) .
Див. Комітет 21e5cfd (29 червня 2015 р.) Від Антуана Делейта (CanardChouChinois ) .
(Об’єднав Хуніо С Хамано - gitster- у комітеті 22dd6eb , 05 жовтня 2015 р.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.