夕方のオフィスで、先週まで動いていた機能が壊れていることに気づき、長く続く変更履歴を前に腕を組んで考え込んでいる開発者

リグレッションの原因コミットをAIと絞り込む|git bisectの進め方

「先週までは、ちゃんと動いてたんですけどね」。 朝会でそう言われて、あなたは git log を開く。先週からのコミットは40件近く。どれが壊したのか、まったく見当がつかない。とりあえずAIに差分の一覧を渡して聞いてみると、「このコミットが怪しいです」と自信ありげに1つ挙げてくる。読んでみると、たしかに怪しくも見える——でも、本当にそれでしょうか。

こういうとき、怪しいコミットを読み込む前に、少しだけ遠回りに見える方法を取るほうが、結果的に早く着きます。あなたの勘が悪いわけでも、AIが役に立たないわけでもありません。「どこで壊れたか」は、推測するより、確かめて絞り込むほうが確実な問いなんです。

結論:リグレッションの原因コミットは、①「動いていたコミット」と「壊れているコミット」を1つずつ確定し、②AIに「壊れていれば失敗する」再現スクリプトを書かせて人が確かめ、③git bisect で二分探索して、原因コミットを事実として特定する、の3手順で探します。AIに「どれが怪しいか」を当てさせるのではなく、探すのは bisect、確かめる道具を作るのと、見つかった差分を読み解くのがAI。この分担がいちばん迷いません。

40件のコミットでも、二分探索なら確かめるのは6回前後で済みます。1件ずつ読むより、ずっと気持ちが楽です。

なぜ「AIに怪しいコミットを当てさせる」とつまずくのか

差分の一覧を渡して「原因はどれ?」と聞くと、AIはだいたい答えを出してくれます。ただ、その答えには気をつけたい癖があります。

AIの推測そのものが悪いわけではありません。推測は「確かめる順番」のヒントには使えても、「答え」には使えない、というだけです。答えは、実際にそのコミットを動かして確かめれば出せます。それを機械的にやってくれるのが git bisect です。

git bisect は「真ん中を試す」をくり返すだけ

横一列に並んだ丸い印の図。左端の緑の丸に「正常」、右端の赤い丸に「異常」、真ん中の丸に虫めがねが重なっていて「ここを試す」とラベルがついている
動いていた地点と壊れている地点のあいだの「真ん中」を試し、結果に合わせて範囲を半分ずつ狭めていくのが二分探索です

git bisect の考え方は、とてもシンプルです。

  1. 「このコミットでは動いていた(good)」と「このコミットでは壊れている(bad)」を1つずつ教える
  2. Gitがその真ん中のコミットをチェックアウトする
  3. 試して、動けば good、壊れていれば bad と答える
  4. 範囲が半分になる。これをくり返す

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 は、出発点がずれていると、ずれたまま正確に間違った答えを出します。始める前に、両端だけは人が確かめます。

ここで「good のはずのコミットでも壊れていた」と分かることがあります。それは失敗ではなく、原因がもっと前にあるという大事な情報です。good をさらに前にずらします。

手順2:AIに再現スクリプトを書かせ、人が確かめる

git bisect run は、指定したスクリプトの終了コードで good / bad を判定します。

終了コードbisect の判定
0good(動いている)
125skip(このコミットは判定できない)
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回ずつ手で走らせます。

  1. bad のコミットで実行 → exit 1 になるか
  2. 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

途中で気をつけたいところが、いくつかあります。

手順4:見つかったコミットを、AIと一緒に読み解く

ここからがAIの二つ目の出番です。原因コミットは事実として特定できているので、AIに聞く問いは「どれが怪しいか」から「このコミットの中の、どの変更が症状につながるか」に変わります。

git bisect で、下のコミットが原因だと特定できました。
(「first bad commit」の表示と、このコミットの差分を貼る)

症状:(例:CSV出力で、日付列が1日ずれる)

1. この差分のうち、症状につながりそうな変更を可能性の高い順に挙げてください
2. それぞれ、なぜそう考えるかを1〜2行で
3. その仮説を確かめるには、どの1行を戻して試せばいいか
推測が含まれる箇所は「推測」と明記してください。

挙がった候補は、1つずつ戻して再現スクリプトを走らせることで確かめられます。ここでも答えを決めるのは、AIの説明ではなく、スクリプトの結果です。直したあとは、その再現スクリプトをテストとして残しておくと、同じ壊れ方を二度としなくて済みます。既存テストをAIにどう渡すかは AIに直させてもリグレッションを出さない既存テストの渡し方 で整理しています。

なお、差分やログを外部のAIサービスに貼るときは、社内の取り決めに沿って、秘密情報や個人情報が混ざっていないかを確かめてからにします。

これができると、何が変わるか

ありがちな落とし穴と、その回避

明日からやること(小さく始める3つ)

  1. 今いちばん気になっている不具合について、「動いていたコミット」を1つ決めて手で確かめる:まだ bisect は回さなくて大丈夫です。
  2. AIに上の頼み方で再現スクリプトを書いてもらい、両端で exit 0 / exit 1 になるか試す:ここまでできれば、もう半分終わっています。
  3. 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、最後に判断するのはあなた。その分担で、「どこで壊れたか分からない」は少しずつ怖くなくなっていきます。

よければ、こちらも

関連用語