git bisectでAI生成コードの回帰コミットを10分で絞る — 自動判定の5コマンド
AIに実装を手伝ってもらうと、変更の速度は上がります。
その一方で、こんな場面も増えてきませんか。
昨日までは動いていた。今日のmainでは壊れている。でも、その間にAI支援のcommitが20個ある。
差分を上から読み、怪しいcommitをAIに聞き、また別のcommitを眺める。これ、調査というより推測ゲームになりがちなんです。
先に結論を言うと、こういう時は git bisect がかなり効きます。
git bisectは、正常だったcommitと壊れているcommitの間を ** 二分探索 ** し、不具合を最初に持ち込んだcommitを絞るGitの標準機能です。さらに判定用スクリプトがあれば、git bisect runで探索を自動化できます。
この記事では、これを ** 「回帰の二境界」 ** と呼びます。
** 回帰の二境界とは、現在のbadと既知のgoodを人間が固定し、その間だけを機械判定で半分ずつ削る調査設計です。 **
犯人探しではなく、境界づくり。
10分のローカル演習から始めて、明日の実務にそのまま持ち込める5段階まで説明します。
更新日: 2026-07-30
この記事が向いている人
- AI支援で変更量が増え、どのcommitから壊れたか分からない
- Gitの基本操作はできるが、
git bisectはまだ使ったことがない - 目視やAIの推測ではなく、再現テストを根拠に調べたい
- チームで再利用できる回帰調査のRunbookを作りたい
反対に、毎回症状が変わる不具合や、正常だったcommitを一つも特定できない障害には、そのままでは向きません。後半で見分け方と回避策も書きます。
まず知っておきたい3つの言葉
回帰
以前は動いていた機能が、後の変更によって動かなくなることです。英語ではregressionと呼びます。
goodとbad
git bisectのgoodとbadは、コード全体の品質評価ではありません。
-
good: ** 今回調べる症状が出ない ** -
bad: ** 今回調べる症状が出る **
ここを曖昧にすると、探索結果も曖昧になります。「ログインできない」を調べる時に、無関係なlint失敗までbad扱いしてはいけない、ということです。
二分探索
候補を一つずつ調べるのではなく、真ん中を調べて範囲を半分に減らす方法です。
たとえば候補が64commitあっても、判定が安定していれば理論上はおおむね6回の判定で境界へ近づけます。大事なのは「64件なら必ず6回で終わる」と約束することではなく、 ** 総当たりより調査回数を減らせる構造 ** です。mergeやskipがある履歴では回数も結果も変わります。
10分で最初の成功を作る
ここでは外部サービスもAPIキーも使いません。5commitの小さなrepositoryを作り、3番目に入った回帰を自動で見つけます。
作業中のrepositoryを汚さないよう、新しい空ディレクトリで試してください。
1. ダミーの履歴を作る
次のスクリプトを setup-demo.sh として保存します。
#!/usr/bin/env bash
set -euo pipefail
demo_dir="bisect-demo"
mkdir "$demo_dir"
cd "$demo_dir"
git init
git config user.email "demo@example.invalid"
git config user.name "Demo User"
for value in 1 2 3 4 5; do
printf '%s\n' "$value" > value.txt
git add value.txt
git commit -m "value ${value}"
done
git log --oneline --reverse
実行します。
chmod +x setup-demo.sh
./setup-demo.sh
cd bisect-demo
value 1からvalue 5まで、5件のcommitが表示されれば準備完了です。
この演習では、value.txtが3以上になったcommitを「回帰」とみなします。
2. good / badを返す判定スクリプトを作る
bisect-demoの中へ test-regression.sh を作ります。
#!/usr/bin/env bash
set -eu
value="$(cat value.txt)"
if [ "$value" -lt 3 ]; then
exit 0
fi
exit 1
終了コードには契約があります。
| 終了コード |
git bisect runの扱い |
使いどころ |
|---|---|---|
0 |
good | 対象症状が出なかった |
1〜127(125を除く) |
bad | 対象症状が再現した |
125 |
skip | build不能などで判定できない |
正直、ここが一番大事です。
テストコマンドが「対象バグ」以外の理由で1を返すと、git bisectはそのcommitをbadと判断します。ネットワーク障害、依存取得失敗、flaky testまで同じ1にすると、きれいに自動化された誤診が完成します。
3. 5コマンドで自動探索する
まず、最初のcommitを既知のgoodとして変数へ入れます。
good_commit="$(git rev-list --reverse HEAD | head -n 1)"
chmod +x test-regression.sh
次の5コマンドが本体です。
git bisect start
git bisect bad HEAD
git bisect good "$good_commit"
git bisect run ./test-regression.sh
git bisect reset
途中でGitがcommitを切り替え、スクリプトを繰り返し実行します。最後に次のような結果が出ます。
<commit-id> is the first bad commit
value 3
これで「3番目の変更から壊れた」を、差分の雰囲気ではなく判定スクリプトで選べました。
最後の git bisect reset は忘れないでください。bisect開始前のbranchへ戻すための後片付けです。
実務では5段階に分ける
ダミーでは動いた。でも実案件は、そんなに単純ではないですよね。
なので実務では、コマンドより前の判断を含めて5段階にします。
1. 再現条件を1本に固定する
「なんか遅い」「時々落ちる」では自動判定できません。まず、goodとbadを機械的に分けられる1本のコマンドへ落とします。
たとえばNode.jsなら、対象テストだけを指定します。
npm test -- --runInBand tests/login-regression.test.js
Pythonなら、対象テストを一つに絞る形です。
python -m pytest tests/test_login.py::test_expired_session -q
最初から全テストを回すと、無関係な失敗がbad判定へ混ざります。まず対象症状だけ。修正候補が見つかってから、全体の回帰テストへ広げる方が安全です。
判定を自動化する前に、同じcommitで最低3回ほど実行し、結果が安定しているか確かめるのがおすすめです。3回という数字はGitの仕様ではなく、flakyな判定を入口で見つけるための実務上の目安です。
2. goodとbadの境界を人間が決める
badは通常、症状が出ている現在のHEADです。
難しいのはgoodです。「先週は動いていた」という記憶ではなく、実際にcheckoutして再現コマンドが通るcommitやrelease tagを選びます。
git switch --detach v1.4.0
npm test -- --runInBand tests/login-regression.test.js
通ったことを確認してから、そのtagをgoodに使います。
git bisect start HEAD v1.4.0
この短縮形では、最初の引数がbad、次の引数がgoodです。向きを逆にしないよう注意してください。
未コミット変更がある状態でcheckoutを繰り返すのは危険です。先にgit status --shortを確認し、必要ならcommit、stash、または別worktreeへ退避します。
3. 判定スクリプトを「対象症状の契約」にする
実務の判定スクリプトでは、単にテストコマンドを呼ぶだけでなく、判定不能を分けます。
#!/usr/bin/env bash
set -u
if ! npm ci --ignore-scripts; then
echo "依存関係を準備できないため、このcommitは判定不能" >&2
exit 125
fi
npm test -- --runInBand tests/login-regression.test.js
test_status=$?
if [ "$test_status" -eq 0 ]; then
exit 0
fi
if [ "$test_status" -eq 1 ]; then
exit 1
fi
echo "対象症状以外の失敗としてskip" >&2
exit 125
これは汎用の完成品ではありません。テストランナーごとに終了コードの意味が違うため、実際のコマンド仕様に合わせて調整してください。
また、古いcommitではlockfileやランタイム要件が変わっていることがあります。毎回依存取得すると遅い場合は、コンテナやキャッシュを使う判断も必要です。ただしキャッシュがcommit間の状態を持ち越すと判定を汚すことがあります。速さより再現性を優先する場面です。
4. git bisect runで探索し、ログを残す
準備ができたら実行します。
git bisect start HEAD v1.4.0
git bisect run ./scripts/check-login-regression.sh
git bisect log > bisect-login.log
git bisect logには、どのrevisionをgood / bad / skipと判定したかが残ります。チームレビューではfirst bad commitのhashだけでなく、このログと判定スクリプトも一緒に残すと、結論を再現しやすくなります。
first bad commitが出たら、すぐ修正へ飛びつかず、差分を確認します。
git show --stat --oneline refs/bisect/bad
git show refs/bisect/bad
git bisectが証明するのは、 ** その判定条件でgoodからbadへ変わる最初のcommit ** です。作者の意図、業務上の根本原因、最善の修正方法までは証明しません。
5. resetし、追加テストで仮説を検証する
調査が終わったら元へ戻ります。
git bisect reset
その後、first bad commitの親とfirst bad commitで再現コマンドを手動実行します。
first_bad="<確認したcommit-id>"
git switch --detach "${first_bad}^"
./scripts/check-login-regression.sh
git switch --detach "$first_bad"
./scripts/check-login-regression.sh
親でgood、対象commitでbadになることを再確認できれば、境界の証拠が強くなります。
ただし、ここでも「この人が悪い」「このcommitをrevertすれば必ず直る」とは言えません。後続commitとの組み合わせやデータ移行が原因なら、単純なrevertが別の障害を作ることもあります。修正方針は人間がレビューして決めます。
人間が決めること、AIへ任せること
AI時代の回帰調査で大事なのは、AIを外すことではありません。責任境界を曖昧にしないことです。
| 項目 | 人間が担当 | AIに任せやすい |
|---|---|---|
| 症状 | 何を不具合と呼ぶか決める | ログから再現条件候補を整理 |
| 境界 | 既知のgood / badを実測で承認 | 候補tagやcommit一覧を整理 |
| 判定 | false positive / negativeを評価 | 判定スクリプトのたたき台作成 |
| 調査 | 実行権限、秘密情報、外部副作用を判断 | bisectログと差分を要約 |
| 修正 | revert / patch / redesignを採用 | 修正案と追加テスト案を比較 |
AIが怪しいcommitを当てること自体は、仮説づくりには使えます。でも、仮説と証拠は別物です。
そのまま使えるプロンプト例3本
プロンプト1: 再現条件を最小化する
次の障害ログから、git bisectで使える「対象症状だけを判定するコマンド」候補を作ってください。
条件:
- 正常なら終了コード0
- 対象症状が再現した時だけ終了コード1
- 環境不備や依存取得失敗は終了コード125
- 外部APIへ書き込みを行わない
- 全テストではなく最小の1テストへ絞る
出力:
1. 再現条件
2. コマンド
3. false positiveの可能性
4. 人間が確認すべき前提
障害ログ:
(機密情報を削除したログを貼る)
プロンプト2: 判定スクリプトをレビューする
次のgit bisect用スクリプトをレビューしてください。
確認観点:
- 0=good、1〜127のうち125以外=bad、125=skipを守っているか
- 対象症状以外の失敗をbadにしていないか
- commit間で残るキャッシュや生成物が判定を汚さないか
- 外部サービスへの副作用がないか
- POSIX shellまたはBashのどちらを前提にするか明記されているか
修正版を出す前に、誤判定シナリオを3つ列挙してください。
スクリプト:
(スクリプトを貼る)
プロンプト3: first bad commitを結論ではなく仮説へ変える
git bisectで得たfirst bad commitの差分を分析してください。
禁止:
- 作者の意図を断定しない
- 差分だけで根本原因と断定しない
- いきなりrevertを推奨しない
出力:
1. 判定条件と関係しそうな差分
2. 原因仮説を最大3つ
3. 各仮説を反証できる追加テスト
4. revert / 最小patch / 設計変更の比較
5. 人間が承認すべき判断
判定条件:
(再現条件)
差分:
(秘密情報を除いたgit showの結果)
うまく効かない4つの条件
1. テストがflaky
同じcommitでgoodとbadが揺れるなら、二分探索の前提が崩れます。
回避策は、まず判定を複数回実行して安定化することです。時刻、乱数、ネットワーク、共有DB、並列実行などの変動要因を外します。安定しないまま自動化しない。これが見分け方です。
2. 履歴途中のcommitがbuildできない
対象バグとは無関係にbuild不能なら、終了コード125でskipできます。
ただしGit公式ドキュメントにもある通り、first badの近くをskipすると、どれが最初のbadか一意に決められない場合があります。その時は候補範囲として報告し、近隣commitを手動検証します。
3. merge履歴の意味が複雑
機能branch内で変更を作ったcommitと、mainへ取り込んだmerge commitは別です。
git bisect start --first-parentを使うとfirst-parentだけをたどる調査もできますが、これは問いを変えます。通常の探索が「どの変更が症状を導入したか」を追うのに対し、first-parent探索は「mainのどの統合点から症状が見えるか」を追いやすくなります。
どちらが正しいかではなく、何を知りたいかを人間が先に決めます。
4. 変更が1〜2commitしかない
この場合は無料で十分というより、bisectを使わなくても十分です。
親commitと現在を直接比較した方が速い。道具を使うことが目的ではありません。候補が増え、総当たりの時間が重くなった時に使う道具です。
チーム導入用の最小Runbook
最後に、チームのIssueや障害テンプレへ貼れる形へ縮めます。
## 回帰調査
- 対象症状:
- 再現コマンド:
- bad commit:
- good commit:
- 同一commitで3回安定したか:
- 判定スクリプト:
- skip条件:
- git bisect log:
- first bad commit:
- 親でgood / 対象でbadを再確認したか:
- 修正判断者:
このテンプレの価値は、情報量ではありません。
「AIが怪しいと言った」から「この条件で境界が変わった」へ、会話の土台を変えられることです。調査を途中で引き継いでも、次の人が同じ判定を再実行できます。
まとめ
AI生成コードが増えた時、差分を読む力はもちろん大切です。でも、変更量が多いほど、最初から全部を読むより ** 機械的に範囲を狭めてから読む ** 方が合理的な場面があります。
手順は5つです。
- 再現条件を1本に固定する
- goodとbadの境界を人間が決める
- 判定スクリプトで0 / bad / 125を分ける
-
git bisect runで探索し、ログを残す -
git bisect reset後、親と対象commitで再確認する
今日10分でやるなら、まずダミーrepositoryでvalue 3がfirst bad commitになるところまで試してください。次に実案件の不具合を一つ選び、 ** 再現コマンドと既知のgood tagを1行ずつ書く ** 。それが次の一歩です。
参考リンク
生成AI活用エンジニア&3児のパパ。AI×開発の実践知を毎日発信しています。次回の実践知はXで受け取れます。