日本語 ▾ Topics ▾ Latest version ▾ git-bisect last updated in 2.50.0

NAME

git-bisect - バイナリサーチでバグを導入したコミットを特定する

概要

git bisect <サブコマンド> <オプション>

説明

このコマンドはさまざまなサブコマンドを取り、サブコマンドごとに異なるオプションを指定できます。

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

このコマンドはバイナリサーチ(2分探索)アルゴリズムを使って、プロジェクト履歴のどのコミットでバグが導入されたかを特定します。まず、バグが含まれていることが分かっている「bad」なコミットと、バグがまだ存在しないことが分かっている「good」なコミットを指定します。すると git bisect はその2点の間のコミットを選び、そのコミットが「good」か「bad」かを尋ねてきます。この範囲を絞り込む操作を繰り返し、最終的にバグを導入した正確なコミットを特定します。

実際には、git bisect はプロジェクトの 任意の 性質が変化したコミットを特定するためにも使えます。たとえば、バグを修正したコミットや、ベンチマークの性能が向上したコミットなどです。このような一般的な用途に対応するため、「good」「bad」の代わりに「old」「new」などの用語を使ったり、独自の用語を指定することもできます。詳細は後述の「用語のカスタマイズ」セクションを参照してください。

基本的なバイセクトコマンド: start, bad, good

例として、バージョン v2.6.13-rc2 で正常に動作していた機能が、どのコミットで壊れたかを調べたいとします。バイセクトセッションは次のように開始します:

$ git bisect start
$ git bisect bad                 # 現在のバージョンは不具合あり
$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 は正常であることが分かっている

bad と good のコミットをそれぞれ1つ以上指定すると、git bisect はその範囲の中間にあるコミットを選択してチェックアウトし、次のようなメッセージを出力します:

バイセクト中: この後テストすべきリビジョンが675件残っています(おおよそ10ステップ)

この時点でチェックアウトされたバージョンをビルドしてテストします。もしそのバージョンが正常に動作する場合は、次のコマンドを入力します

$ git bisect good

もしそのバージョンに不具合がある場合は、次のコマンドを入力します

$ git bisect bad

すると git bisect は次のようなメッセージを表示します

バイセクト中: この後テストすべきリビジョンが337件残っています(おおよそ9ステップ)

この手順を繰り返します。ツリーをビルドしてテストし、正常なら git bisect good、不具合があれば git bisect bad を実行して、次にテストすべきコミットを指定します。

最終的に調査すべきリビジョンがなくなると、コマンドは最初に不具合が導入されたコミットの説明を表示します。このとき refs/bisect/bad 参照がそのコミットを指すようになります。

Bisect をリセット

bisect セッション終了後、bisection 状態をクリーンアップして元の HEAD に戻すには、次のコマンドを実行します:

$ git bisect reset

デフォルトでは、これにより git bisect start を実行する前にチェックアウトされていたコミットにツリーが戻ります。(新しく git bisect start を実行した場合も同様に、古い bisection 状態をクリーンアップします。)

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

$ git bisect reset <commit>

例えば、git bisect reset bisect/bad は最初のバッドリビジョンをチェックアウトし、git bisect reset HEAD は現在のバイセクションコミットに留まり、全くコミットを切り替えないことになります。

代替の用語

場合によっては、不具合を引き起こしたコミットではなく、ある「古い」状態と「新しい」状態の間で変更を引き起こしたコミットを探すことがあります。例えば、特定の修正が導入されたコミットを探している場合や、ソースコードのファイル名がすべて会社の命名規則に変換された最初のコミットを探している場合などです。あるいは他のどんな場合でも同様です。

このような場合、「変更前の状態」と「変更後の状態」を指すのに「good」と「bad」という用語を使うと非常に紛らわしくなることがあります。そこで代わりに、「good」と「bad」の代わりにそれぞれ「old」と「new」という用語を使用できます(ただし、一つのセッション内で「good」と「bad」を「old」と「new」と混在させることはできないことに注意してください。)

この、より一般的な使用方法では、ある特性を持つ「new」コミットと、その特性を持たない「old」コミットを git bisect に提供します。git bisect がコミットをチェックアウトするたびに、そのコミットがその特性を持っているかどうかをテストします。持っている場合は、そのコミットを「new」とマークし、そうでない場合は「old」とマークします。bisection が完了すると、git bisect はどのコミットがその特性を導入したかを報告します。

「good」と「bad」の代わりに「old」と「new」を使用するには、引数としてコミットを指定せずに 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-newgit bisect terms --term-bad は、探している変更よりも新しいコミットをどう呼ぶかを確認するために使用できます。

「bad」/「good」または「new」/「old」の代わりに独自の用語を使用したい場合は、次のように bisection を開始することで、任意の名前を選択できます(ただし、resetstart などの既存の bisect サブコマンドは除きます)

git bisect start --term-old <古い状態の用語> --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 <古い状態の用語>git bisect <新しい状態の用語> を使用します。

Bisect の可視化/表示

bisection プロセス中に、現在残っている候補を gitk で確認するには、次のコマンドを実行します(サブコマンド viewvisualize の代わりに使用することもできます):

$ 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 ログと bisect リプレイ

リビジョンを good または bad としてマークした後、次のコマンドを実行すると、これまでに何が行われたかを確認できます:

$ git bisect log

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

$ git bisect reset
$ git bisect replay that-file

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

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

例えば:

$ git bisect good/bad			# 前のラウンドが good または bad だった
Bisecting: 337 revisions left to test after this (roughly 9 steps)
$ git bisect visualize			# おっと、これは興味深くない
$ git reset --hard HEAD~3		# 提案された内容の3つ前の
					# リビジョンを試す

その後、選択したリビジョンをコンパイルしてテストし、通常の方法でそのリビジョンを good または bad としてマークします。

Bisect スキップ

自分で近くのコミットを選択する代わりに、次のコマンドを実行することで Git にそれを行わせることもできます:

$ git bisect skip                 # 現在のバージョンはテストできない

ただし、探しているコミットに隣接するコミットをスキップすると、Git はそれらのコミットのうちどれが最初の異常なコミットだったかを正確に特定できなくなります。

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

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

これは bisect プロセスに対して、v2.5 より後のコミットから v2.6 までを含むすべてのコミットをテストしないよう指示します。

範囲の最初のコミットもスキップしたい場合は、次のコマンドを実行します:

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

これは bisect プロセスに対して、v2.5 から v2.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)は、現在のソースコードが good/old の場合はコード 0 で終了し、現在のソースコードが bad/new の場合は 125 を除く 1〜127(両端を含む)の間のコードで終了する必要があることに注意してください。

他の終了コードは bisect プロセスを中断します。exit(-1) で終了するプログラムは $? = 255 となることに注意してください(exit(3) マニュアルページを参照)。これは値が & 0377 で切り詰められるためです。

現在のソースコードがテストできない場合は、特別な終了コード 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_HEAD という名前の参照を更新し、テスト対象のコミットを指すようにします。

各ステップで実行するテストが作業ツリーのチェックアウトを必要としない場合、このオプションが役立ちます。

リポジトリがベアリポジトリの場合は、--no-checkout が自動的に有効になります。

--first-parent

マージコミットが現れた場合、最初の親コミットのみをたどります。

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

このオプションは、マージされたブランチに壊れたコミットやビルドできないコミットが含まれていても、マージ自体は問題なかった場合に、誤検出(偽陽性)を防ぐのに特に有効です。

  • v1.2 と HEAD の間でビルドが壊れた原因を自動的にバイセクトする例:

    $ git bisect start HEAD v1.2 --      # HEADは不具合あり、v1.2は正常
    $ git bisect run make                # "make"でアプリをビルド
    $ git bisect reset                   # バイセクトセッションを終了
  • origin と HEAD の間でテスト失敗の原因を自動的にバイセクトする例:

    $ git bisect start HEAD origin --    # HEADは不具合あり、originは正常
    $ git bisect run make test           # "make test"でビルドとテストを実行
    $ git bisect reset                   # バイセクトセッションを終了
  • テストケースが失敗する原因を自動的にバイセクトする例:

    $ cat ~/test.sh
    #!/bin/sh
    make || exit 125                     # ビルド失敗時はスキップ
    ~/check_test_case.sh                 # テストケースが通るか確認
    $ git bisect start HEAD HEAD~10 --   # 最後の10件の中に原因あり
    $ git bisect run ~/test.sh
    $ git bisect reset                   # バイセクトセッションを終了

    ここでは test.sh というカスタムスクリプトを使っています。このスクリプトでは、make が失敗した場合はそのコミットをスキップします。check_test_case.sh はテストケースが通れば exit 0、失敗すれば exit 1 を返すようにしてください。

    バイセクトや make、テスト処理とスクリプトの間で予期しない干渉を防ぐため、test.shcheck_test_case.sh の両方をリポジトリの外に置くほうが安全です。

  • 一時的な修正(ホットフィックス)を適用しながら自動バイセクトする例:

    $ cat ~/test.sh
    #!/bin/sh
    
    # ホットフィックスブランチをマージして作業ツリーを調整
    # その後ビルドを試みる
    if	git merge --no-commit --no-ff hot-fix &&
    	make
    then
    	# プロジェクト固有のテストを実行し、その結果を報告
    	~/check_test_case.sh
    	status=$?
    else
    	# テストできないことを呼び出し元に伝える
    	status=125
    fi
    
    # 次のコミットへクリーンに切り替えられるよう調整を元に戻す
    git reset --hard
    
    # 制御を戻す
    exit $status

    これは各テスト実行前にホットフィックスブランチの修正を適用します。たとえば、ビルドやテスト環境が変わり、古いリビジョンには修正が必要だが新しいリビジョンには不要な場合などに使います。(ホットフィックスブランチはバイセクト対象のすべてのリビジョンに含まれるコミットから分岐していることを確認し、マージで余計な変更が入らないようにするか、git merge の代わりに git cherry-pick を使ってください。)

  • テストケースが失敗する原因を自動的にバイセクトする例:

    $ git bisect start HEAD HEAD~10 --   # 最後の10件の中に原因あり
    $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
    $ git bisect reset                   # バイセクトセッションを終了

    テストを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                   # バイセクトセッションを終了

    この場合、git bisect run が終了すると、bisect/bad は少なくとも1つの親を持ち、git pack objects が要求する意味で到達可能なグラフが完全にたどれるコミットを指すようになります。

  • リグレッション(不具合の混入)ではなく修正(バグ修正)を探す

    $ git bisect start
    $ git bisect new HEAD    # 現在のコミットを「修正あり」としてマーク
    $ git bisect old HEAD~10 # 10個前のコミットを「修正なし」としてマーク

    または:

    $ 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

Part of the git[1] suite

scroll-to-top