
リグレッションの原因コミットをAIと絞り込む|git bisectの進め方
「先週までは、ちゃんと動いてたんですけどね」。 朝会でそう言われて、あなたは git log を開く。先週からのコミットは40件近く。どれが壊したのか、まったく見当がつかない。とりあえずAIに差分の一覧を渡して聞いてみると、「このコミットが怪しいです」と自信ありげに1つ挙げてくる。読んでみると、たしかに怪しくも見える——でも、本当にそれでしょうか。
こういうとき、怪しいコミットを読み込む前に、少しだけ遠回りに見える方法を取るほうが、結果的に早く着きます。あなたの勘が悪いわけでも、AIが役に立たないわけでもありません。「どこで壊れたか」は、推測するより、確かめて絞り込むほうが確実な問いなんです。
結論:リグレッションの原因コミットは、①「動いていたコミット」と「壊れているコミット」を1つずつ確定し、②AIに「壊れていれば失敗する」再現スクリプトを書かせて人が確かめ、③git bisect で二分探索して、原因コミットを事実として特定する、の3手順で探します。AIに「どれが怪しいか」を当てさせるのではなく、探すのは bisect、確かめる道具を作るのと、見つかった差分を読み解くのがAI。この分担がいちばん迷いません。
40件のコミットでも、二分探索なら確かめるのは6回前後で済みます。1件ずつ読むより、ずっと気持ちが楽です。
なぜ「AIに怪しいコミットを当てさせる」とつまずくのか
差分の一覧を渡して「原因はどれ?」と聞くと、AIはだいたい答えを出してくれます。ただ、その答えには気をつけたい癖があります。
- 見た目が怪しい変更に引っぱられる:大きな差分、名前が関係ありそうなファイル、エラーメッセージに出てくる単語を含む変更が選ばれがちです。実際の原因は、関係なさそうな設定ファイル1行や、依存ライブラリの更新だった、ということがよくあります。
- 渡していない情報は見えない:一覧に入れなかったコミット、ロックファイルの中身、環境変数の変更などは、AIにとって存在しないのと同じです。
- 「それらしい説明」が先にできてしまう:もっともらしい理由つきで1つ挙げられると、人のほうもそれを前提に読み始めてしまいます。外れていたら、そこから半日が消えます。
AIの推測そのものが悪いわけではありません。推測は「確かめる順番」のヒントには使えても、「答え」には使えない、というだけです。答えは、実際にそのコミットを動かして確かめれば出せます。それを機械的にやってくれるのが git bisect です。
git bisect は「真ん中を試す」をくり返すだけ

git bisect の考え方は、とてもシンプルです。
- 「このコミットでは動いていた(good)」と「このコミットでは壊れている(bad)」を1つずつ教える
- Gitがその真ん中のコミットをチェックアウトする
- 試して、動けば good、壊れていれば bad と答える
- 範囲が半分になる。これをくり返す
1回ごとに範囲が半分になるので、確かめる回数はコミット数のわりにとても少なく済みます。
| 間のコミット数 | 確かめる回数の目安 |
|---|---|
| 40件 | 約6回 |
| 200件 | 約8回 |
| 1,000件 | 約10回 |
(2を何回かけるとその件数を超えるか、が目安です。2⁶=64、2⁸=256、2¹⁰=1,024。途中でビルドできないコミットを飛ばすと、少し増えることがあります)
基本の流れは次のとおりです。
git bisect start
git bisect bad # 今のコミット(HEAD)は壊れている
git bisect good v2.3.0 # このタグの時点では動いていた
# → Gitが真ん中をチェックアウトするので、試して結果を答える
git bisect good # 動いた
git bisect bad # 壊れていた
git bisect skip # ビルドできない等で判定できない
# → 「<コミットID> is the first bad commit」と出たら特定完了
git bisect reset # 元のブランチに戻る
手で1回ずつ答えてもかまいませんが、ここで「再現スクリプト」があると、全部を自動で回せます。そして、そのスクリプトづくりこそ、AIが得意なところです。
手順1:「動いていた」と「壊れている」を1つずつ確定する
bisect は、出発点がずれていると、ずれたまま正確に間違った答えを出します。始める前に、両端だけは人が確かめます。
- bad(壊れている):今のブランチの先頭で、実際に再現することを確かめる。
- good(動いていた):「先週は動いていた」の先週がどのコミットかを決める。リリースタグ、前回デプロイしたコミット、前回CIが緑だったコミットなどが候補です。決めたら、そのコミットをチェックアウトして本当に動くことを1回確かめます。
ここで「good のはずのコミットでも壊れていた」と分かることがあります。それは失敗ではなく、原因がもっと前にあるという大事な情報です。good をさらに前にずらします。
手順2:AIに再現スクリプトを書かせ、人が確かめる
git bisect run は、指定したスクリプトの終了コードで good / bad を判定します。
| 終了コード | bisect の判定 |
|---|---|
| 0 | good(動いている) |
| 125 | skip(このコミットは判定できない) |
| 1〜127(125を除く) | bad(壊れている) |
| それ以外(128以上など) | bisect を中断する |
この決まりに沿ったスクリプトを、AIに書いてもらいます。
リグレッションの原因コミットを git bisect run で探したいです。
再現スクリプトを書いてください。まだ bisect は実行しません。
## 症状
(例:CSV出力で、日付列が1日ずれる。v2.3.0 では正しかった)
## 再現手順(分かる範囲で)
(例:テストデータ sample.csv を読み込み、export コマンドで出力し、2行目の日付を見る)
## スクリプトの条件
- 症状が出ていなければ exit 0、出ていれば exit 1 で終わる
- 依存の入れ直しやビルドに失敗したときは、症状の有無と区別して exit 125 で終わる
- 本番やステージングのDB・外部API・メール送信など、外に影響することは一切しない
- 実行のたびに結果が変わらないようにする(日時・乱数・ネットワークに頼らない)
- 何を判定しているかを、スクリプト冒頭のコメントに1〜3行で書く
出てきたスクリプトは、bisect にかける前に両端で1回ずつ手で走らせます。
- bad のコミットで実行 → exit 1 になるか
- good のコミットで実行 → exit 0 になるか
この2つがそろわないスクリプトで bisect を回すと、どこかで判定がずれて、関係のないコミットを犯人にしてしまいます。ここが、この手順でいちばん大事な確認です。
あわせて、スクリプトが外に何かを書き込んでいないかも目で見ます。bisect は古いコミットを何度もチェックアウトして実行するので、マイグレーションが走る、共有の環境を書き換える、といった処理が混ざっていると困ったことになります。コマンドの安全確認の考え方は AIにコマンドを書かせて事故らない|破壊的コマンドを弾く確認の型 にまとめています。再現手順そのものが曖昧なときは、先に AIに障害の再現手順を整理させる|報告と切り分けを速く から始めると早いです。
手順3:bisect run で二分探索する
スクリプトが両端で正しく動いたら、あとは任せます。
git bisect start HEAD v2.3.0 # bad と good をまとめて指定
git bisect run ./repro.sh
最後に <コミットID> is the first bad commit と、そのコミットの情報が表示されます。終わったら、記録を残してから元に戻します。
git bisect log > bisect-log.txt # どのコミットをどう判定したかの記録
git bisect reset
途中で気をつけたいところが、いくつかあります。
- 依存関係が変わるコミットをまたぐ:ロックファイルが変わっているのに入れ直さずにテストすると、正しく判定できません。スクリプトの最初で依存の入れ直しとビルドを行い、失敗したら 125 で抜けるようにしておくと安心です。
- たまにしか再現しない不具合:1回の実行では good と言い切れないので、判定がぶれます。数回くり返して1回でも出たら bad にする、など判定を固めてから回します。ぶれ方そのものに悩んでいるなら たまに落ちるテスト(Flaky)の原因をAIに切り分けさせる手順 も参考になります。
- マージコミットが多い履歴:取り込まれたブランチの途中のコミットまで調べに行くと、ビルドが通らないコミットに当たりやすくなります。
git bisect start --first-parentで、メインの流れのコミットだけを順にたどる方法もあります(比較的新しいGitで使える指定です。使えるかは手元のバージョンで確かめてください)。 - 作業中の変更:始める前にコミットするか
git stashで退避しておきます。bisect はチェックアウトをくり返すので、未保存の変更があるとうまく進みません。
手順4:見つかったコミットを、AIと一緒に読み解く
ここからがAIの二つ目の出番です。原因コミットは事実として特定できているので、AIに聞く問いは「どれが怪しいか」から「このコミットの中の、どの変更が症状につながるか」に変わります。
git bisect で、下のコミットが原因だと特定できました。
(「first bad commit」の表示と、このコミットの差分を貼る)
症状:(例:CSV出力で、日付列が1日ずれる)
1. この差分のうち、症状につながりそうな変更を可能性の高い順に挙げてください
2. それぞれ、なぜそう考えるかを1〜2行で
3. その仮説を確かめるには、どの1行を戻して試せばいいか
推測が含まれる箇所は「推測」と明記してください。
挙がった候補は、1つずつ戻して再現スクリプトを走らせることで確かめられます。ここでも答えを決めるのは、AIの説明ではなく、スクリプトの結果です。直したあとは、その再現スクリプトをテストとして残しておくと、同じ壊れ方を二度としなくて済みます。既存テストをAIにどう渡すかは AIに直させてもリグレッションを出さない既存テストの渡し方 で整理しています。
なお、差分やログを外部のAIサービスに貼るときは、社内の取り決めに沿って、秘密情報や個人情報が混ざっていないかを確かめてからにします。
これができると、何が変わるか
- 「どこで壊れたか」の議論が終わる:推測どうしをぶつけ合う時間が、確かめる時間に変わります。答えはコミットIDという形で、誰にでも見せられます。
- 報告がしやすくなる:「v2.3.0 以降の◯◯というコミットで、この変更が原因でした。bisect の記録はこちらです」と言えると、上にもチームにも安心してもらえます。
- AIへの頼み方が変わる:「当てて」ではなく「確かめる道具を作って」「この事実を読み解いて」と頼めるようになります。AIの出力を鵜呑みにしない姿勢は、AIのハルシネーションの見抜き方|開発現場で気づく型 とも地続きです。
ありがちな落とし穴と、その回避
- good のコミットを確かめずに始める:「たぶん動いていた」で始めると、答えがずれる。両端は必ず1回ずつ手で確かめる。
- 再現スクリプトを両端で試さない:判定がずれたまま自動で走り、無関係なコミットが犯人になる。bad で 1、good で 0 を先に確認する。
- ビルド失敗を bad として扱う:症状と関係ない理由で bad になり、範囲が狂う。判定できないときは 125(skip)。
- AIの「怪しい」を答えにする:推測は確かめる順番のヒントまで。答えは bisect とスクリプトで出す。
- reset を忘れる:途中のコミットにいるまま作業を始めてしまう。終わったら
git bisect reset。
明日からやること(小さく始める3つ)
- 今いちばん気になっている不具合について、「動いていたコミット」を1つ決めて手で確かめる:まだ bisect は回さなくて大丈夫です。
- AIに上の頼み方で再現スクリプトを書いてもらい、両端で exit 0 / exit 1 になるか試す:ここまでできれば、もう半分終わっています。
git bisect runを回し、git bisect logを残してから reset する:見つかったコミットは、AIと一緒に差分を読みます。
コピーして使う「原因コミットの絞り込み チェックリスト」
# 原因コミットの絞り込み(git bisect)チェックリスト
## 1. 始める前
- [ ] 壊れているコミット(bad)で、症状が再現することを確かめた
- [ ] 動いていたコミット(good)を決め、実際に動くことを確かめた
- [ ] 作業中の変更をコミットまたは stash で退避した
## 2. 再現スクリプト
- [ ] 症状なしで exit 0、症状ありで exit 1 になる
- [ ] ビルド・依存の入れ直しに失敗したときは exit 125 で抜ける
- [ ] 本番・共有環境・外部API・DBに書き込まない
- [ ] 日時・乱数・ネットワークで結果が変わらない
- [ ] bad のコミットで exit 1、good のコミットで exit 0 を手で確かめた
## 3. 二分探索
- [ ] git bisect run で回した(たまに再現する症状は判定を固めてから)
- [ ] 「first bad commit」の結果と git bisect log を保存した
- [ ] git bisect reset で元のブランチに戻った
## 4. 見つかったあと
- [ ] AIには「どの変更が症状につながるか」を、推測と明記させて挙げさせた
- [ ] 候補は1つずつ戻して、再現スクリプトで確かめた
- [ ] 再現スクリプトをテストとして残した
- [ ] 外部AIに貼った差分・ログに、秘密情報が混ざっていないことを確かめた
最後に
「どのコミットで壊れたか分からない」は、ベテランでも普通に起きることです。勘が働かないのは、あなたの力不足ではありません。 だから、勘に頼らなくていい方法を一つ持っておく。動いていた地点と、壊れている地点。その2つさえ確かめられれば、あとは真ん中を試すだけで、答えのほうから近づいてきてくれます。

今日は、いちばん気になっている不具合の「動いていたコミット」を1つ確かめるところからで十分です。探すのは bisect、道具を作って読み解くのはAI、最後に判断するのはあなた。その分担で、「どこで壊れたか分からない」は少しずつ怖くなくなっていきます。
よければ、こちらも
- エラーの読み解きから始めたいなら スタックトレースをAIに読ませる|バグ調査を速くする渡し方
- 直したあとに同じ壊れ方をさせないために AIに直させてもリグレッションを出さない既存テストの渡し方
- AIとの修正ループから抜け出したいときは AIとの修正ループが終わらない|堂々巡りから抜ける手順
- 履歴をまたぐ作業でつまずいたら AIにGitのコンフリクト解消を手伝わせる|壊さないマージの確認手順