web-dev-qa-db-ja.com

git bisectの使用方法は?

git bisectが素晴らしいと言っている記事をいくつか読みました。しかし、私はネイティブスピーカーではないので、なぜそれが素晴らしいのか理解できません。

誰かがいくつかのコードサンプルでデモしてください:

  1. どうやって使うのですか?
  2. svn blameのようなものですか?
385
IAdapter

git bisectの背後にある考え方は、特定の回帰を見つけるために履歴内でバイナリ検索を実行することです。次の開発履歴があることを想像してください。

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

プログラムがcurrentリビジョンで正常に動作していないこと、およびリビジョン0で動作していたことがわかります。そのため、リグレッションは、コミット12345currentのいずれかで発生した可能性があります。

各コミットをチェックアウトしてビルドし、リグレッションが存在するかどうかを確認することができます。多数のコミットがある場合、これには長い時間がかかる可能性があります。これは線形検索です。バイナリ検索を行うことで、より良い結果を得ることができます。これが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 bisect bad、存在しない場合はgit bisect goodを使用して、gitにこのリビジョンのステータスを伝える必要があります。

回帰がコミット4で導入されたと仮定しましょう。その後、このリビジョンには回帰は存在せず、gitに通知します。

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

その後、別のコミットをチェックアウトします。 4または5のいずれか(コミットが2つしかないため)。 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 ... >

この単純な状況では、4つのバージョン(3451)ではなく、3つのバージョン(234)のみをテストする必要がありました。これは小さな勝利ですが、これは私たちの歴史が非常に小さいためです。検索範囲がNコミットの場合、線形検索で約N/2コミットする代わりに、git bisectで1 + log2 Nコミットをテストする必要があります。

回帰を引き起こしたコミットを見つけたら、それを調べて問題を見つけることができます。これが完了したら、git bisect resetを使用して、git bisectコマンドを使用する前にすべてを元の状態に戻します。

576

git bisect run自動二分法

テストに問題がなければ、終了ステータスが0の自動化された./testスクリプトがある場合は、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で追跡されている場合、2分割中の以前のコミットで消えないことを前提としています。

ツリー内のスクリプトをツリーからコピーして、PATHのような変数で遊んで、代わりにそこから実行することで、多くの場合逃げることができることがわかりました。

もちろん、testが依存するテストインフラストラクチャが古いコミットに違反する場合、解決策はなく、コミットを1つずつテストする方法を決定して、手動で作業を行う必要があります。

その他のヒント

masterに戻るのではなく、bisectの後の最初の失敗したコミットにとどまります。

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_BAD_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

これは、対応するrefを持つコミットのみを示し、ノイズを大幅に減らしますが、タイプの自動生成されたrefを含みます:

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

どのコミットが良いか悪いかをマークします。

コマンドを試してみたい場合は、 このテストリポジトリ を検討してください。

失敗は速い、成功は遅い

時々:

  • 障害は迅速に発生します。最初のテストが壊れる
  • 成功にはしばらく時間がかかります。壊れたテストに合格し、気にしない他のすべてのテストが続きます

それらの場合、例えば失敗は常に5秒で発生し、実際にテストをより具体的にするのが面倒であれば、次のようにtimeoutを使用できます。

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

これは、timeout124を終了し、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でテスト済み。

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
110
Geoffrey Hale

さらにポイントを追加するには:

バグが特定のファイルに由来することがわかっている場合は、git bisect startへのファイル名またはパスを指定できます。たとえば、リグレッションの原因となった変更がcom/workingDirディレクトリにあることを知っていた場合、git bisect start com/workingDirを実行できます。これは、このディレクトリの内容を変更したコミットのみがチェックされることを意味します。

また、特定のコミットが良いか悪いかを判断するのが難しい場合は、git bisect skipを実行できますが、これは無視されます。他にも十分なコミットがある場合、git bisectは代わりに別のコミットを使用して検索を絞り込みます。

37
Nicks

$ git bisect .. bascicallyデバッグ用のGitツール。 「Git Bisect」は、前回の(既知の)コミット以降、前のcommitsを経由してデバッグします。バイナリ検索を使用してこれらすべてのコミットを実行し、リグレッション/バグを引き起こしたコミットに到達します。

$ git bisect start#bisectの開始

$ git bisect bad#現在のコミット(v1.5)にリグレッション/設定「不良」ポイントがあると述べる

$ git bisect good v1.0#最後の正常なコミット(リグレッションなし)に言及

この「悪い」点と「良い」点の言及は、git bisect(バイナリ検索)中間要素を選択するのに役立ちます(コミットv1.3)。コミットv1.3でリグレッションがある場合、それを新しい「不良」ポイントとして設定します。つまり、(Good-> v1.0 and Bad-> v1.3))

$ git bisect bad

または同様に、コミットv1.3にバグがない場合は、それを新しい「良い点」として設定します(* Good-> v1.3 and Bad-> v1.6)。

$ git bisect good
11
Nabeel Ahmed

注:goodおよびbadという用語は、特定のプロパティの有無にかかわらずコミットをマークするために使用できる唯一の用語ではありません。

Git 2.7(2015年第4四半期)では、新しい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は、どのコミットがプロパティを導入したかを報告します。


commit 06e6a74commit 21b55ecommit fe67687 (2015年6月29日)by Matthieu Moy(moy を参照してください。
Antoine Delaite(CanardChouChinois による commit 21e5cfd (2015年6月29日)を参照してください。
浜野潤二-gitster- in commit 22dd6eb 、2015年10月5日)

1
VonC