PRにならないまま消えかける作業を未pushブランチ検出で見張る

  • #Git
  • #worktree
  • #自動化
  • #運用監視

今回やったこと

AIエージェントが worktree で作業する運用では、変更が作業ブランチの中に閉じたまま、PRにもならず、mainにも届かない状態が起こる。今回の対象は、すでにPRになった作業の滞留ではない。そもそも一度も push されず、GitHub側から見えないブランチを見張るための番犬を追加した。

きっかけは、Issue が参照していたドキュメントとサイトの実体が origin/main に無く、未 push のローカルブランチに閉じ込められていた調査だった。該当ブランチの具体的な名前や分岐点のコミットは、公開記事では省略する。PRは1件も作られていなかった。同様の事例がないか調べると、別の未 push ブランチにも記事や学習ノート、スクリプトが残っていた。

「未 push」だけでは判定しない

最初に考えやすい判定は、ローカルブランチが origin に存在しないことだ。しかし、それだけでは誤検知が多い。別ブランチで同じ変更が取り込まれた場合や、手コピーで成果物だけが main に届いた場合、ブランチ自体が未 push でも実害は残っていないからだ。

そこで判定を二段に分けた。

判定見るもの意味
1origin に同名ブランチが無い一度も push されていない、またはリモート側で削除された可能性
2ブランチが触ったファイルの中身を main と比較成果が main に届いていない可能性

2段目では、ファイルが main に無い absent と、存在するが内容が違う differs を分ける。追加されたファイルだけを数えると、既存ファイルの変更を落としてしまう。実際、追加だけを対象にした棚卸しでは約20本だったものが、差分全体を見ると約40本になった。既存ファイルの改修のみで構成されるブランチが「実害なし」と誤分類されていたため、追加・削除・変更をまとめて確認する設計へ改めた。

存在判定にも注意が要る。git rev-parse <rev>:<path> はパスごとに終了コードを扱う必要があり、大量のパスを調べるには非効率である。そのため、存在確認は一括判定できる cat-file --batch-checkmissing を使う。監視の精度は、比較ロジック以前に「存在しない」を正しく表現できるかで決まる。

監視対象から外すものを先に決める

番犬は、見つけたものをすべて異常として通知する仕組みではない。稼働中の worktree は、既定では1日以内に触られているものを対象外にする。作業途中のブランチを「消えた成果」として鳴らせば、通知の信頼性が落ちるからだ。

画像や動画など raw/ 配下のメディアも除外する。これらは Git に入れない運用が正常で、正本は別の保管先へ退避する。反対に prompts.json のようなサイドカー JSON は追跡対象として残す。.gitignore で無視されている生成物も、Gitに聞いて除外する。除外規則をスクリプトに重複して持つと、リポジトリ側のルールが増えたときに監視だけ古くなるためだ。

通知先も「今日増えた異常」と「積み残しの棚卸し」に分ける。前回から増えたブランチは異常として通知し、初回の全件や一定期間ごとの再掲はログへ回す。古い在庫を毎回「今日起きたこと」として送ると、本当に新しい異常が埋もれる。

やってみてわかったこと

未 push ブランチの監視は、GitHub APIだけでは完結しない。origin に存在しないブランチは、PR一覧にも出ないため、ブランチを切った機械のローカル refs を見る必要がある。また、検出した約40本を自動で救出する仕組みにしてはいけない。古いだけのブランチを戻すと退行する可能性があり、differs が未マージ編集なのか、mainの進行による陳腐化なのかは中身を読んで仕分ける必要がある。

実装済みの番犬がするのは、候補を決定的に並べ、分岐点から main がどれだけ進んだかを添えて、人間が確認できる状態にするところまでだ。救出・破棄・公開は別の判断として残す。無人運用で重要なのは、成果を自動的に動かすことではなく、閉じこもった成果を見える場所へ出すことでもある。

実装ステータスと更新履歴

実装済み・稼働中。参照した実装は、孤立ブランチの監視スクリプトとテストコードである。

  • 更新履歴: 2026-09-12 初稿。
  • 訂正履歴: なし。