Git
English ▾ トピック ▾ 最新バージョン ▾ git-bisectは2.44.0で最後に更新されました

名前

git-bisect - バグを導入したコミットをバイナリサーチで見つける

概要

git bisect <subcommand> <options>

説明

このコマンドは、様々なサブコマンドと、サブコマンドに応じた異なるオプションを取ります。

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

このコマンドは、プロジェクトの履歴の中でどのコミットがバグを導入したかを発見するために、二分探索アルゴリズムを使用します。最初に、バグが含まれていることがわかっている「悪い」コミットと、バグが導入される前に存在していたことがわかっている「良い」コミットを指定します。すると、git bisect は、これらの2つの端点の間にあるコミットを選択し、選択されたコミットが「良い」か「悪い」かを尋ねます。変更を導入したコミットを正確に見つけるまで、範囲を狭め続けます。

実際、git bisect は、プロジェクトの**あらゆる**プロパティを変更したコミットを見つけるために使用できます。例えば、バグを修正したコミットや、ベンチマークのパフォーマンスを向上させたコミットなどです。このより一般的な使用方法をサポートするために、「良い」と「悪い」の代わりに「古い」と「新しい」という用語を使用することも、独自の用語を選択することもできます。詳細については、以下の「代替用語」セクションを参照してください。

基本的なbisectコマンド: start, bad, good

例として、プロジェクトのバージョン v2.6.13-rc2 で動作していたことがわかっている機能を壊したコミットを見つけようとしているとします。bisectセッションは次のように開始します。

$ 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 は、そのコミットを指したままになります。

Bisect reset

bisectセッションの後、二分探索の状態をクリーンアップし、元のHEADに戻るには、次のコマンドを実行します。

$ git bisect reset

デフォルトでは、これは git bisect start の前にチェックアウトされていたコミットにツリーを戻します。(新しい git bisect start も、古い二分探索の状態をクリーンアップするので、同じことを行います。)

オプションの引数を指定すると、代わりに別のコミットに戻ることができます。

$ git bisect reset <commit>

たとえば、git bisect reset bisect/bad は最初の悪いリビジョンをチェックアウトしますが、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 を使用して古い用語のみを取得できます。git bisect terms --term-new および git bisect terms --term-bad を使用すると、目的の変更よりも新しいコミットの呼び方を学ぶことができます。

「悪い」/「良い」または「新しい」/「古い」の代わりに独自の用語を使用したい場合は、二分探索を開始することで、好きな名前(resetstartなどの既存のbisectサブコマンドを除く)を選択できます。

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

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

Bisect visualize/view

現在残っている容疑者を *gitk* で表示するには、二分探索プロセス中に次のコマンドを実行します(サブコマンド `view` は `visualize` の代わりに使用できます)。

$ git bisect visualize

Gitは、さまざまな環境変数を通じてグラフィカル環境を検出します。UnixシステムのX Window System環境で設定される `DISPLAY`。対話型デスクトップセッションでCygwinで設定される `SESSIONNAME`。Msys2とGit for Windowsで設定される `MSYSTEM`。対話型デスクトップセッションでmacOSで設定される `SECURITYSESSIONID`。

これらの環境変数がどれも設定されていない場合、代わりに *git log* が使用されます。`-p` や `--stat` などのコマンドラインオプションも指定できます。

$ git bisect visualize --stat

Bisect log と bisect replay

リビジョンを良いまたは悪いとマークした後、次のコマンドを実行して、これまでに何が行われたかを表示します。

$ git bisect log

リビジョンのステータスを指定する際に間違いを犯したことが判明した場合は、このコマンドの出力をファイルに保存し、編集して間違ったエントリを削除してから、次のコマンドを実行して修正された状態に戻すことができます。

$ git bisect reset
$ git bisect replay that-file

コミットのテストの回避

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

例えば

$ 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

次に、選択したリビジョンをコンパイルしてテストし、その後、通常どおりリビジョンを良いまたは悪いとマークします。

Bisect skip

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

$ git bisect skip                 # Current version cannot be tested

ただし、探しているコミットに隣接するコミットをスキップすると、Gitはそれらのコミットのどちらが最初の悪いコミットであったかを正確に判断できません。

1つのコミットだけでなく、範囲表記を使用してコミットの範囲をスキップすることもできます。例えば

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

これは、`v2.5` 以降、`v2.6` までのコミットをテストすべきではないことをbisectプロセスに指示します。

範囲の最初のコミットもスキップしたい場合は、次のコマンドを実行することに注意してください。

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

これは、bisect プロセスに対して、v2.5v2.6 の間のコミット(両端を含む)をスキップする必要があることを指示します。

bisect start により多くのパラメータを与えることでbisectを絞り込む

bisect start コマンドを実行する際に pathspec パラメータを指定することで、追跡している問題にツリーのどの部分が関係しているかを知っている場合、試行回数をさらに減らすことができます。

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

事前に複数の正常なコミットがわかっている場合は、bisect start コマンドを実行する際に、不良なコミットの直後にすべての正常なコミットを指定することで、bisect の範囲を絞り込むことができます。

$ 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

bisect run

現在のソースコードが正常か不良かを判断できるスクリプトがある場合は、次のコマンドを実行することで bisect を行うことができます。

$ git bisect run my_script arguments

スクリプト(上記の例では my_script)は、現在のソースコードが正常/古い場合はコード 0 で終了し、現在のソースコードが不良/新しい場合は 1 から 127(両端を含む)の間のコード(ただし 125 を除く)で終了する必要があることに注意してください。

その他の終了コードは bisect プロセスを中止させます。 exit(-1) を介して終了するプログラムは、値が & 0377 で切り捨てられるため、$? = 255 を残します(exit(3) マニュアルページを参照)。

特別な終了コード 125 は、現在のソースコードをテストできない場合に使用してください。スクリプトがこのコードで終了すると、現在のリビジョンはスキップされます(上記の git bisect skip を参照)。125 は、この目的で使用できる最も意味のある値として選択されました。126 と 127 は POSIX シェルによって特定のエラー状態を通知するために使用されるためです(127 はコマンドが見つからない場合、126 はコマンドが見つかったが実行できない場合です。これらの詳細は、bisect run に関してはスクリプトの通常のエラーであるため、重要ではありません)。

bisect セッション中に、テストされているリビジョンに一時的な変更(例:ヘッダーファイルの s/#define DEBUG 0/#define DEBUG 1/、または「このコミットがないリビジョンには、この bisect が関心のない別の問題を回避するためにこのパッチを適用する必要がある」)を適用したいことがよくあります。

このような状況に対処するために、内部の _git bisect_ がテストする次のリビジョンを見つけた後、スクリプトはコンパイル前にパッチを適用し、実際のテストを実行し、その後、リビジョン(必要なパッチが適用されている可能性がある)がテストに合格したかどうかを判断してから、ツリーを元の状態に戻すことができます。最後に、スクリプトは実際のテストのステータスで終了し、git bisect run コマンドループが bisect セッションの最終的な結果を決定できるようにする必要があります。

オプション

--no-checkout

bisect プロセスの各反復で新しい作業ツリーをチェックアウトしないでください。代わりに、BISECT_HEAD という名前の参照を更新して、テストするコミットを指すようにします。

このオプションは、各ステップで実行するテストでチェックアウトされたツリーが必要ない場合に役立ちます。

リポジトリが bare の場合、--no-checkout が想定されます。

--first-parent

マージコミットを見つけた場合は、最初の親コミットのみをたどります。

ブランチのマージによって導入されたリグレッションを検出する際に、マージコミットはバグの導入として識別され、その祖先は無視されます。

このオプションは、マージされたブランチに壊れたコミットまたはビルドできないコミットが含まれていたが、マージ自体は問題なかった場合に、誤検知を回避するのに特に役立ちます。

  • v1.2 と HEAD の間の壊れたビルドを自動的に bisect する

    $ 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
  • origin と HEAD の間のテストの失敗を自動的に bisect する

    $ 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
  • 壊れたテストケースを自動的に bisect する

    $ 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 する必要があります。

    bisect、make、およびテストプロセスとスクリプト間の相互作用を防ぐため、test.shcheck_test_case.sh の両方がリポジトリ外にある方が安全です。

  • 一時的な変更(ホットフィックス)を使用して自動的に bisect する

    $ 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

    これは、各テスト実行の前にホットフィックスブランチからの変更を適用します。たとえば、ビルド環境またはテスト環境が変更されたため、古いリビジョンに新しいリビジョンに既に存在する修正が必要になる場合があります。(ホットフィックスブランチが、bisect しているすべてのリビジョンに含まれているコミットに基づいていることを確認して、マージが多すぎないようにするか、git merge の代わりに git cherry-pick を使用してください。)

  • 壊れたテストケースを自動的に bisect する

    $ 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

    これは、テストを 1 行で記述すれば、実行スクリプトなしで実行できることを示しています。

  • 破損したリポジトリ内のオブジェクトグラフの正常な領域を見つける

    $ 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_ が終了すると、bisect/bad は、_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 を使用します。

GIT

git[1] スイートの一部

scroll-to-top