0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git bisectでAI生成コードの回帰コミットを10分で絞る — 自動判定の5コマンド

0
Posted at

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 対象症状が出なかった
1127125を除く) 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. 再現条件を1本に固定する
  2. goodとbadの境界を人間が決める
  3. 判定スクリプトで0 / bad / 125を分ける
  4. git bisect runで探索し、ログを残す
  5. git bisect reset後、親と対象commitで再確認する

今日10分でやるなら、まずダミーrepositoryでvalue 3がfirst bad commitになるところまで試してください。次に実案件の不具合を一つ選び、 ** 再現コマンドと既知のgood tagを1行ずつ書く ** 。それが次の一歩です。

参考リンク

生成AI活用エンジニア&3児のパパ。AI×開発の実践知を毎日発信しています。次回の実践知はXで受け取れます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?