Gitで「いつからバグが入ったのか分からない」状況に直面したら、git bisectを使って問題を導入したコミットを高速に特定できます。bisectは履歴を二分探索で辿り、正常(good)と不正(bad)をマークしながら原因コミットを絞り込む仕組みです。ここでは手動手順と自動化(bisect run)、実務のコツまで一気に解説します。
準備:前提と基本の流れ
バグが再現する最新のコミット(bad)と、バグが存在しないことが分かっている古いコミット(good)を用意します。二点が決まれば、その間の履歴を二分探索で検査できます。作業ツリーはクリーンにしてから開始しましょう(未コミット変更があると切り替え時に失敗します)。
# 未コミット変更を退避(任意)
git stash -u
# bisect開始
git bisect start
git bisect bad # 現在のHEADがバグを含む場合
git bisect good <good_commit_hash_or_tag> # 例: v1.2.0 など
以降、Gitが自動で過去と現在の中間点へチェックアウトします。テストして結果に応じてgood/badを返します。
# テストしてバグが出なかったら
git bisect good
# テストしてバグが再現したら
git bisect bad
数回繰り返すと、原因コミット(first bad commit)が特定されます。終了したら状態を戻します。
git bisect reset
git stash pop # 退避していた変更を戻す(使っていた場合)
手動検証の実例とチェックポイント
1. 最新のmainにバグあり → git bisect start
、git bisect bad
。
2. 安定版タグv1.2.0では再現しない → git bisect good v1.2.0
。
3. Gitが中間コミットへ移動 → アプリをビルド/起動し再現確認。
4. 再現すればbad
、再現しなければgood
を入力。
5. 特定後にgit show
で差分を精査、修正方針を決める。
自動化:git bisect runでテストを自動判定
テストや再現手順をスクリプト化できるなら、git bisect run
で全工程を自動化できます。スクリプトは「正常=終了コード0、不正=非0」を返すようにします。
# 例: scripts/test_bug.sh(UNIX系)
#!/usr/bin/env bash
set -euo pipefail
# 依存のインストールやビルド(必要ならキャッシュ化)
npm ci >/dev/null 2>&1 || exit 125 # 125は「このコミットは判定不可」の合図
npm run build >/dev/null 2>&1 || exit 125
# 実際の再現テスト:失敗=バグあり → 非0、成功=バグなし → 0
npm test --silent && exit 0 || exit 1
# 自動 bisect
git bisect start
git bisect bad
git bisect good v1.2.0
git bisect run bash scripts/test_bug.sh
判定不可(skip)やマージ込みの履歴への対処
ビルドが壊れているなど「このコミットでは判定不能」というケースはskipします。二分探索は継続します。
git bisect skip
# または自動化スクリプトで exit 125 を返して「判定不可」を伝える
マージコミットが多い履歴で原因がブランチ側にある場合、bisectは通常どおり機能しますが、原因が「マージ結果」でのみ現れることもあります。必要に応じてgit bisect visualize
で探索範囲の履歴を眺め、再現条件を見直します。
テストの安定化と高速化のコツ
- フレーク対策:テストを冪等にし、非決定的要素(時間・乱数・外部API)をモック化。失敗時に1回だけリトライするラッパーを用意すると誤判定を減らせます。
- ビルド時間の短縮:依存インストールをキャッシュする、不要なタスクを切る、ユニットレベルの最小再現テストに絞る。
- 環境固定:Node/Python/Javaなどのバージョンを
.tool-versions
やDockerで固定し、各コミットで環境差による誤差を無くす。 - 部分テスト:
npm test --
やpytest -k
、mvn -Dtest=...
などで対象を最小化。
よくあるエラーと対処
- 作業ツリーがクリーンでない:
git stash -u
で退避してからbisect。 - サブモジュールのズレ:
git submodule update --init --recursive
をスクリプトに組み込む。 - 依存が古くてビルド不能:そのコミットは
skip
。多発する場合は探索範囲の基準goodを新しめに見直す。 - 再現手順が人手依存:できる限り自動テスト化。難しければCLI操作をexpect/Playwright等でスクリプト化。
原因コミット特定後のチェックリスト
git show <commit>
で差分とメッセージを精査(周辺コミットとの連携を確認)。- 回帰テストを追加(CIに組み込み再発防止)。
- 修正コミットを作成し、必要なら
cherry-pick
で安定ブランチへ反映。 - リリースノートに影響範囲と対策を記載。
まとめ
git bisectは「バグの導入点」を二分探索で高速に絞り込む強力なツールです。最新=bad、既知の安定点=goodをセットし、手動でgood/badを返すか、bisect run
で自動判定すれば、最小回数のチェックで原因コミットに到達できます。フレークを排除したテストと環境固定を徹底し、特定後は回帰テストを追加して再発を防ぐ、までをワンセットで運用しましょう。