git-bisect

コマンド名

「git-bisect」:バイナリ検索を使用し、バグを引き起こすコミットを見つける

概要

git bisect <subcommand> <options>

説明

このコマンドでは様々なサブコマンドとサブコマンドによって異なるオプションを取ります。

git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd>...
git bisect help

このコマンドではバイナリ検索アルゴリズムを使用し、プロジェクト履歴でバグを発生させたコミットを見つけます。まず最初にバグが含まれていることがわかっている「問題がある」コミットとバグが発生する前にあることがわかっている「問題がない」コミットを見つけるために使用します。次にgit bisectはこれら2つのエンドポイント間のコミットを選択し、選択したコミットが「問題がない」か「問題がある」かを尋ねます。変更した正確なコミットが見つかるまで、範囲の絞り込み続けます。
実際にはgit bisectを使用し、プロジェクトのプロパティを変更したコミットを見つけることができます。例えば、バグを修正したコミットまたはベンチマークのパフォーマンスを向上させたコミットです。このより一般的な使用についてサポートするため、「問題がない」と「問題がある」の代わりに「既存」と「新規」という用語を使用することの選択も可能です。詳細については以下の「代替用語」のセクションを参照してください。

基本的なバイセクトコマンド:開始、問題がある、問題がない
例として、プロジェクトのバージョンv2.6.13-rc2で機能することがわかっている機能を壊したコミットを見つけてみましょう。 次のようにバイセクトセッションを開始します。

$ git bisect start
$ git bisect bad                 # Current version is bad
$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 is known to be good

少なくとも1つの問題があるコミットと1つの問題がないコミットを特定できれば、git bisectはその履歴範囲の中でコミットを選択し、それをチェックアウトし、次のようなものを出力します。

Bisecting: 675 revisions left to test after this (roughly 10 steps)

チェックアウトしたバージョンをコンパイルしてテストする必要があります。そのバージョンが正しく機能する場合、次のように入力します

$ git bisect good

そのバージョンが機能しない場合、次のように入力します。
$ git bisect bad
次に、git bisectは次のように応答します。

Bisecting: 337 revisions left to test after this (roughly 9 steps)

このプロセスを繰り返します。ツリーをコンパイルし、テストし、それが問題がないか、問題があるかに応じて、git bisect good または git bisect bad を実行して、テストが必要となる次のコミットを要求します。
最終的には検査するリビジョンがなくなり、コマンドは最初の問題があるコミットの説明を出力します。refs/bisect/badはそのコミットをポイントしたままとなります。

バイセクトリセット

バイセクトセッションの後、バイセクト状態をクリーンアップして元のヘッドに戻すには、次のコマンドを実行します。

$ git bisect reset

デフォルトではこれによって、git bisectが開始する前にチェックアウトされたコミットにツリーが戻ります(新規のgit bisect startも既存のバイセクト状態をクリーンアップするため、これを実行することになります)。
オプション引数を使用すると、代わりに別のコミットに戻ることができます。

$ git bisect reset <commit>

例えば、git bisect reset bisect/badは最初の問題のあるリビジョンをチェックアウトしますが、while git bisect reset HEADは現在のバイセクトコミットのままにし、コミットの切り替えを全く行いません。

代替用語

機能しなかったコミットではなく、他の「既存」状態と「新規」状態の間で変化を引き起こしたコミットを探す場合があります。例えば、特定の修正を行ったコミットを探す場合があります。またはソースコードのファイル名がすべて御社の用語基準に最終的に変換された最初のコミットを探す場合があるかもしれません。
このような場合、「問題がない」と「問題がある」という用語を「変更前の状態」と「変更後の状態」を示すのに使用すると、非常に混乱する可能性があります。したがって、その代わりに「問題がない」と「問題がある」の代わりに、それぞれ「既存」と「新規」という用語を使用することができます(ただし、1回のセッションで「問題がない」と「問題がある」を「既存」と「新規」に混在させることはできないことに注意してください)。
このより一般的な使用法ではgit bisectにいくつかのプロパティを持つ「新規」コミットとそのプロパティを持たない「既存」コミットを与えます。git bisectがコミットをチェックアウトするたびに、そのコミットにプロパティがあるかどうかをテストします。含まれている場合はコミットを「新規」としてマークします。それ以外の場合、「既存」としてマークします。バイセクトが行われると、git bisectはどのコミットがプロパティを導入したかをレポートします。
「問題がない」と「問題がある」の代わりに「既存」と「新規」を使用するには、引数としてのコミットなしでgit bisect startを実行してから、次のコマンドを実行してコミットを追加する必要があります。

git bisect old [<rev>]

コミットは求められた変更の前であったことを示すため、次のコマンドを実行します。

git bisect new [<rev>...]

もしくはそれが後だったことを示すために、現在使用されている用語を思い出させるため、次のコマンドを実行します。

git bisect terms

既存(新規)用語はgit bisect terms --term-old もしくはgit bisect terms --term-goodで取得することができます。
「問題がある」もしくは「問題がない」、または「新規」もしくは「既存」の代わりに独自の用語を使用する場合、バイセクトを開始することで任意の名前を選択できます(resetstart、…などの既存のバイセクトサブコマンドを除く)。

git bisect start --term-old <term-old> --term-new <term-new>

例えば、パフォーマンスの低下をもたらすコミットを探している場合、次のコマンドを使用できます。

git bisect start --term-old fast --term-new slow

またはバグを修正したコミットを探している場合、次のコマンドを実行します。

git bisect start --term-new fixed --term-old broken

それから、コミットをマークするためにof git bisect goodgit bisect badの代わりに git bisect <term-old>git bisect <term-new> を使用します。

バイセクトの視覚化や表示

「gitk」に現在残っているものを確認するために、バイセクトプロセス中に次のコマンドを実行します(visualizeの代わりにviewサブコマンドを使用できます)。

$ git bisect visualize

環境変数が設定されていない場合、代わりに「gitlog」が使用されます-p--stat. DISPLAYなどのコマンドラインオプションを指定することもできます。

$ git bisect visualize --stat

バイセクトログとバイセクトリプレイ

リビジョンを「問題がない」または「問題がある」とマークした後、次のコマンドを実行して、これまでに行われたことを示します。

$ git bisect log

リビジョンのステータスの指定に誤りがあることに気付いた場合、このコマンドの出力をファイルを保存し、編集して誤りがあるエントリを削除してから、次のコマンドを実行して、修正した状態に戻すことができます。

$ git bisect reset
$ git bisect replay that-file

コミットのテストを回避する

バイセクトセッションの途中で、提案されたリビジョンがテストに適していないことがわかった場合(例えば、ビルドに失敗し、失敗が追跡しているバグとは何の関係もないことがわかっている場合)、 近くのコミットを手動で選択し、代わりにそれをテストすることができます。
例えば、次のコマンドです。

$ git bisect good/bad			# previous round was good or bad.
Bisecting: 337 revisions left to test after this (roughly 9 steps)
$ git bisect visualize			# oops, that is uninteresting.
$ git reset --hard HEAD~3		# try 3 revisions before what
							# was suggested

そして選択したリビジョンをコンパイルしてテストし、その後、通常の方法でリビジョンを「問題がない」または「問題がある」としてマークします。

バイセクトスキップ

近くのコミットを選択する代わりに、次のコマンドを実行して、Gitにそれを実行するように依頼できます。

$ git bisect skip                 # Current version cannot be tested

ただし、探しているコミットに隣接するコミットをスキップすると、Gitはそれらのコミットのどれが最初の問題があるコミットであったかを正確に知ることができなくなります。
範囲表記を使用して、1つのコミットだけでなく、範囲のコミットをスキップすることもできます。 例えば、以下のコマンドです。

$ git bisect skip v2.5..v2.6

これはv2.5以降、v2.6までのコミットをテストしてはならないことをバイセクトプロセスに伝えます。範囲における最初のコミットもスキップする場合、次のコマンドを実行することに注意してください。

$ git bisect skip v2.5 v2.5..v2.6

これはv2.5v2.6 (両端を含む)の間のコミットをスキップする必要があることをバイセクトプロセスに通知します。

バイセクトを開始する多くのパラメータを与えることによりバイセクトは削減します

追跡している問題にツリーのどの部分が関係しているかがわかっている場合、bisect startコマンドを発行する時にパスパラメーターを指定することで、トライアル回数をさらに減らすことができます。

$ git bisect start -- arch/i386 include/asm-i386

事前に一つ以上の問題がないコミットがわかっている場合、bisect startコマンドを実行する時に、問題があるコミットの直後にすべての問題がないコミットを指定することで、バイセクトスペースを絞り込むことができます。

$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
# v2.6.20-rc6 is bad
# v2.6.20-rc4 and v2.6.20-rc1 are good

バイセクトの実行

現在のソースコードが問題がない、もしくは問題があるを判断できるスクリプトがある場合、次のコマンドを発行してバイセクトを行うことができます。

$ git bisect run my_script arguments

スクリプト(上記の例ではmy_script)は現在のソースコードが問題がないもしくは既存の場合はコード0で終了し、現在のソースコードが問題があるもしくは新規の場合は125を除いて1〜127(両端を含む)のコードで終了する必要があることに注意してください。
その他の終了コードはバイセクトプロセスを中止します。exit(-1)を介して終了するプログラムは$? = 255を残し(exit(3)のマニュアルページをご参照ください)、値は& 0377となることに注意してください。
現在のソースコードをテストできない場合、特別な終了コード125を使用する必要があります。スクリプトがこのコードで終了する場合、現在のリビジョンはスキップされます(上記のgit bisect skipをご参照ください)。126と127はPOSIXシェルによって特定のエラーステータスを通知するために使用されるため、125がこの目的で使用する最も適切な値として選択されています(127はコマンドが見つかりません。126はコマンドが見つかりましたが実行可能ではありません。bisect runに関する限り、スクリプト上の通常のエラーであるためこれらの詳細は重要ではありません)。
バイセクトセッション中に一時的な変更を加えたい場合があります(例えば、ヘッダーファイルのs /#define DEBUG 0 /#define DEBUG 1 /、または「このコミットがないリビジョンではこのバイセクトが関連しない別の問題に機能するために適用されるパッチを必要」)はテストされているリビジョンに適用されます。
このような状況に対処するため、内部の「git bisect」がテストする次のリビジョンを見つけた後、スクリプトはコンパイル前にパッチを適用し、実際のテストを実行し、その後リビジョン(必要なパッチを含む)がテストに合格したかどうかを判断し、 そしてツリーを元の状態に戻します。最後にスクリプトは実際のテストのステータスで終了して、git bisect runコマンドループがバイセクトセッションの最終的な結果を決定できるようにする必要があります。

オプション

--no-checkout

バイセクトプロセスの各反復において新しい作業ツリーをチェックアウトしないでください。その代わりに、BISECT_HEADという名前の特別な参照を更新して、テストする必要のあるコミットを示すようにします。
このオプションは各ステップで実行するテストでチェックアウトされたツリーが必要ない場合に役立つことがあります。
リポジトリがベアの場合、--no-checkoutが想定されます。

--first-parent

マージコミットを確認したら、最初のペアレントコミットのみを実行します。
ブランチのマージによって導入されたリグレッションを検出する際、マージコミットはバグの導入として識別され、その祖先は無視されます。
このオプションはマージされたブランチに壊れたコミットまたはビルド不可能なコミットが含まれていますが、マージ自体は問題ない場合に誤検知を回避するのに特に役立ちます。

  • v1.2とヘッドの間で壊れたビルドを自動的にバイセクトします。
    $ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
    $ git bisect run make                # "make" builds the app
    $ git bisect reset                   # quit the bisect session
    
  • オリジンとヘッドの間のテスト失敗を自動的にバイセクトします。
    $ git bisect start HEAD origin --    # HEAD is bad, origin is good
    $ git bisect run make test           # "make test" builds and tests
    $ git bisect reset                   # quit the bisect session
    
  • 壊れたテストケースを自動的にバイセクトします。
    $ cat ~/test.sh
    #!/bin/sh
    make || exit 125                     # this skips broken builds
    ~/check_test_case.sh                 # does the test case pass?
    $ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
    $ git bisect run ~/test.sh
    $ git bisect reset                   # quit the bisect session
    

    ここではtest.shカスタムスクリプトを使用します。このスクリプトではmakeが失敗した場合、現在のコミットをスキップします。check_test_case.shはテストケースに合格した場合はexit 0 とし、合格しなかった場合はexit 1 となります。

  • test.sh とcheck_test_case.sh の両方がリポジトリの外部にある場合、バイセクト、プロセスの作成とテスト、およびスクリプトの間の相互作用を防ぐ方が安全となります。
  • 一時的な変更で自動的にバイセクトします(ホットフィックス)
    $ cat ~/test.sh
    #!/bin/sh
    
    # tweak the working tree by merging the hot-fix branch
    # and then attempt a build
    if	git merge --no-commit --no-ff hot-fix &&
    	make
    then
    # run project specific test and report its status
    ~/check_test_case.sh
    status=$?
    else
    # tell the caller this is untestable
    status=125
    fi
    
    # undo the tweak to allow clean flipping to the next commit
    git reset --hard
    
    # return control
    exit $status
    

    これにより各テスト実行の前にホットフィックスブランチからの変更が適用されます。ビルドまたはテスト環境が変更されたために、既存のリビジョンで新規のリビジョンにすでに修正が必要となる場合があります(ホットフィックスブランチがバイセクトしているすべてのリビジョンに含まれているコミットに基づいていることを確認し、マージがあまりプルされないようにするか、git mergeの代わりにgit cherry-pickを使用してください)。
    Automatically bisect a broken test case: 壊れたテストケースを自動的にバイセクトします。

    $ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
    $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
    $ git bisect reset                   # quit the bisect session
    

    これはテストをシングルラインで記述した場合、実行スクリプトなしで実行できることを示しています。

  • 壊れたレポジトリでオブジェクトグラフの問題のない領域を見つけます
    $ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout
    $ git bisect run sh -c '
    	GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
    	git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$ &&
    	git pack-objects --stdout >/dev/null <tmp.$
    	rc=$?
    	rm -f tmp.$
    	test $rc = 0'
    
    $ git bisect reset                   # quit the bisect session
    

    この場合、「git bisect run」が終了すると、バイセクト/問題があるは到達可能なグラフが「git pack objects」に必要な完全にトラバース可能な少なくとも1つのペアレントを持つコミットを参照します。

  • コードのリグレッションではなく、修正を探します。
    $ git bisect start
    $ git bisect new HEAD    # current commit is marked as new
    $ git bisect old HEAD~10 # the tenth commit from now is marked as old
    

    または

    $ git bisect start --term-old broken --term-new fixed
    $ git bisect fixed
    $ git bisect broken HEAD~10
    

    ヘルプを取得

    git bisectを使用して簡単な使用法の説明を取得し、git bisect helpもしくはgit bisect -h を使用して詳細の使用法の説明を取得します。

    参照

    Fighting regressions with git bisect, git-blame[1].

    GIT

    git[1]パッケージソフトの一部

git公式ドキュメント

bisect