英語 ▾ トピック ▾ 最新バージョン ▾ 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 は、プロジェクトの任意のプロパティを変更したコミットを見つけるために使用できます。例えば、バグを修正したコミット、またはベンチマークのパフォーマンスを向上させたコミットなどです。このより一般的な使い方をサポートするため、「good」と「bad」の代わりに「old」と「new」という用語を使用することも、独自の用語を選択することもできます。詳細については、下記の「Alternate terms」セクションを参照してください。

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

例として、プロジェクトのバージョン 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 はそのコミットを指したままになります。

Bisectのリセット

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

$ git bisect reset

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

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

$ git bisect reset <commit>

例えば、git bisect reset bisect/bad は最初の悪いリビジョンをチェックアウトし、git bisect reset HEAD は現在のバイセクトコミットに留まり、コミットの切り替えを一切行いません。

代替用語

時として、破損を導入したコミットを探しているのではなく、ある「old」状態と「new」状態の間に変化をもたらしたコミットを探している場合があります。例えば、特定の修正を導入したコミットを探しているかもしれません。あるいは、ソースコードのファイル名が最終的にすべて会社の名付け標準に変換された最初のコミットを探しているかもしれません。あるいは何でもよいです。

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

このより一般的な使用法では、git bisect にある特性を持つ「new」コミットと、その特性を持たない「old」コミットを指定します。git bisect がコミットをチェックアウトするたびに、そのコミットがその特性を持っているかどうかをテストします。持っている場合はそのコミットを「new」としてマークし、そうでない場合は「old」としてマークします。バイセクトが完了すると、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-new および git bisect terms --term-bad は、探索対象の変更よりも新しいコミットをどのように呼ぶかを知るために使用できます。

「bad」/「good」または「new」/「old」の代わりに独自の用語を使用したい場合は、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の視覚化/表示

バイセクション処理中に 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ログとBisectリプレイ

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

$ 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

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

Bisectのスキップ

自分で近くのコミットを選択する代わりに、次のコマンドを実行してGitに任せることができます。

$ git bisect skip                 # Current version cannot be tested

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

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

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

これは、バイセクトプロセスに対し、v2.5 以降 v2.6 まで(v2.6 を含む)のコミットはテストすべきではないことを伝えます。

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

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

これは、v2.5v2.6 の間のコミット(両端を含む)をスキップすべきであることをバイセクトプロセスに伝えます。

bisect start にさらにパラメータを与えることでバイセクションを絞り込む

追跡している問題にどのツリーの部分が関わっているかを知っている場合、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

Bisectの実行

現在のソースコードが良いか悪いかを判断できるスクリプトがある場合、次のコマンドを実行してバイセクトできます。

$ git bisect run my_script arguments

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

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

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

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

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

オプション

--no-checkout

バイセクション処理の各イテレーションで新しい作業ツリーをチェックアウトしません。代わりに、BISECT_HEAD という名前のリファレンスを更新して、テストすべきコミットを指すようにします。

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

リポジトリがベアである場合、--no-checkout が仮定されます。

--first-parent

マージコミットに遭遇した場合、最初の親コミットのみを追跡します。

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

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

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

    $ 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の間でテストの失敗を自動的にバイセクトする

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

    バイセクト、make、テストのプロセスとスクリプト間の相互作用を防ぐために、test.shcheck_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 を使用してください。)

  • 壊れたテストケースを自動的にバイセクトする

    $ 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