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

基本的な二分探索コマンド: 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という参照は、そのコミットを指したままになります。

二分探索のリセット

二分探索セッションの後、二分探索の状態をクリーンアップし、元の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-newgit 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>を使用します。

二分探索の視覚化/表示

二分探索プロセス中にgitkで現在残っている容疑者を見るには、以下のコマンドを発行します(サブコマンドviewvisualizeの代替として使用できます)。

$ git bisect visualize

Gitは、X Window System環境のUnixシステムで設定されるDISPLAY、インタラクティブデスクトップセッションでCygwinで設定されるSESSIONNAME、Msys2およびGit for Windowsで設定されるMSYSTEM、macOSのインタラクティブデスクトップセッションで設定される可能性のあるSECURITYSESSIONIDなど、様々な環境変数を通じてグラフィカル環境を検出します。

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

$ 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はそれらのコミットのうちどれが最初の悪いコミットであったかを正確に特定することができません。

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

$ 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にさらにパラメータを与えて二分探索を短縮する

追跡している問題にどのツリーの一部が関係しているかが分かっている場合、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を参照)。125は、この目的のために使用する最高の実用的な値として選ばれました。なぜなら、126と127はPOSIXシェルによって特定のエラー状態を通知するために使用されるためです(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と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で終了する必要があります。

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

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

    $ 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

    これは、テストを一行で記述すれば、実行スクリプトがなくても済むことを示しています。

  • 破損したリポジトリでオブジェクトグラフの良い領域を見つける

    $ 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