git bisect run でバグを入れたコミットを探す:終了コード 0・125・127 の意味

git bisect が二分探索で最初の bad コミットを見つける仕組みと、bisect run が終了コード 0 を good、1〜124 と 126〜127 を bad、125 を skip、それ以外を中断として扱うルールを Git 公式ドキュメントで整理します。

開発Gitバージョン管理テスト

前回のリリースでは通っていたテストがいまは落ちる。その間にコミットが数百個あれば、1つずつ確かめるのは現実的ではありません。git bisect はこの範囲を二分探索で絞り込み、問題を最初に持ち込んだコミットを見つけます。git bisect run に判定用のスクリプトを渡せば、good や bad を自分で入力する必要もありません。ただし、Git がスクリプトの終了コードをどう読むかを知らないと、無関係なコミットが犯人として出てくることがあります。

この記事は Git 2.56.0(2026年9月28日)の git-bisect 公式ドキュメントと、git-scm.com に掲載されている解説文書 git-bisect-lk2009 に沿っています。

基本の流れ

バグのあるコミット(bad)を1つと、バグが入る前のコミット(good)を1つ伝えると始まります。

git bisect start
git bisect bad                 # 現在のバージョンにはバグがある
git bisect good v2.6.13-rc2    # このバージョンは正常だった

Git はその範囲の真ん中あたりのコミットをチェックアウトし、次のように知らせます。

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

そのバージョンをビルドしてテストし、git bisect good か git bisect bad で答えると、残りの候補はおよそ半分ずつ減ります。676個ほどの候補が約10ステップで終わるのは 2¹⁰ = 1024 だからです。確かめるコミットがなくなると、Git は最初の bad コミットを表示し、refs/bisect/bad がそのコミットを指したまま残ります。終わったら git bisect reset で開始前の HEAD に戻ります。

履歴が一直線で、判定が good から bad へ一度だけ切り替わるとすれば、これはソート済み配列で条件が初めて真になる位置を探す問題と同じです。二分探索の挿入位置で扱った bisect_left が見つけるのと同じ境界です。

bisect run が終了コードを読むルール

git bisect start HEAD v1.2 --   # HEAD は bad、v1.2 は good
git bisect run make             # make でアプリをビルドする
git bisect reset

git bisect run はコミットごとにコマンドを実行し、終了コードでそのコミットを判定します。

終了コード 扱い
0 good(old)
1〜127(125 を除く) bad(new)
125 テストできないコミットとして飛ばす(skip)
それ以外 bisect そのものを中断

ここで間違いが起きやすくなります。1つ目に、exit(-1) で終わったプログラムは値が & 0377 で切り詰められて終了コードが 255 になるので、bad ではなく中断として扱われます。2つ目に、126 と 127 は POSIX シェルが「実行できない」と「コマンドが見つからない」に使う値ですが、bisect run にとっては 1〜127 の範囲にある普通の失敗にすぎません。スクリプトが 127 で終われば、そのコミットは bad と判定されます。テストツールが古いコミットにまだ存在しなかったせいで失敗した場合でも、Git はそれをバグとして受け取ります。

ビルドすらできないコミットは 125 で外す必要があります。公式ドキュメントの例は次のとおりです。

#!/bin/sh
make || exit 125                     # ビルドできないコミットは飛ばす
~/check_test_case.sh                 # テストケースが通るか

公式ドキュメントはテストスクリプトをリポジトリの外に置くよう勧めており、例でも ~/ に置いています。bisect はコミットを切り替えるたびに作業ツリーを書き換えるので、リポジトリ内で追跡されているスクリプトは、古いコミットでは存在しないか中身が違う可能性があります。

飛ばしたコミットが多いとき

skip にも代償があります。探しているコミットのすぐ隣のコミットを飛ばすと、Git はそのうちどれが最初の bad コミットなのかを正確に言えなくなります。その場合は候補のコミットを複数表示して止まります。

二分探索が成り立つ条件

git-bisect-lk2009 は、コミットグラフには最初の bad コミットが複数ありうると述べ、範囲を分ける最適なコミットを選ぶアルゴリズムを説明するときだけ、最初の bad コミットを1つと仮定しています。実際に大事なのは、判定の基準が、探索する履歴に沿って good から bad へ切り替わる地点を役に立つ形で示せるかどうかです。バグが入って直って再び入ったり、ビルド結果によって出たり消えたりすると判定が行ったり来たりし、bisect が示す境界は原因と無関係だったり、実行のたびに変わったりすることがあります。判定を入力し間違えたときは、git bisect log をファイルに保存して誤った行を消し、git bisect reset のあと git bisect replay <ファイル> で戻せます。

知っておくと便利なオプションもあります。

  • バグを直したコミットを探すときのように「good/bad」がしっくりこない場合は、引数なしで git bisect start を実行し、git bisect old と git bisect new で印を付けます。
  • --first-parent はマージコミットで1番目の親だけをたどります。マージされたブランチにビルドできないコミットがあってもマージ自体は問題なかった場合、マージコミットを犯人として示し、この種の誤検出を避けます。
  • --reset-when-found は最初の bad コミットが見つかると後片付けまで行います。2.56.0 のドキュメントにはありますが 2.55.0 のドキュメントにはないオプションなので、使う前に git --version を確認してください。

コミット範囲を表す A..B 記法は git log と git diff の .. と … で説明しています。

参考にしたドキュメント

リンクと内容は2026年10月5日に確認しました。

ブログへ