name: delete-merged-branches description: > 指定した基準ブランチ(develop / main など)に取り込まれたローカルおよび origin 上の リモートブランチを安全に削除する。通常 merge は祖先判定、squash/rebase merge は GitHub のマージ済み PR の headRefOid 一致で判定。あわせて、基準ブランチ向けに リポジトリ全体のクローズ済み(未マージ)PR のリモートブランチも削除候補にする (基準以外向けの sub-PR や GitHub Copilot 等が作ってクローズしたブランチも掃除できる)。"マージ済みブランチを削除", "delete merged branches", "ローカル/リモートブランチを掃除", "ブランチ整理" などで使う。
マージ済みローカル・リモートブランチの削除
git branch --merged は祖先関係のみ見るため、squash/rebase merge では未マージ扱いになる。本スキルでは祖先判定に加え、GitHub の merged PR の headRefOid 一致でローカル・リモート両方の削除対象を安全に決める。あわせて、クローズされた(未マージ)PR のブランチはリモートのみ削除候補とする(ローカルは対象にしない)。クローズ PR は gh pr list --state closed でリポジトリ全体から取得するため、基準以外のブランチ向けに作られた sub-PR(例: copilot/sub-pr-*)も候補になる。主 remote は origin を前提とする。
推奨: スクリプトで一括実行
事前に git fetch origin --prune と gh auth status が通ることを確認したうえで、以下で候補列挙・確認・削除まで行う。
git fetch origin --prune
./scripts/delete-merged-branches.sh [基準ブランチ] [--dry-run]
- 基準ブランチ: 省略時はローカルに
developがあればdevelop、なければorigin/HEADの短縮名(例: main)。 - --dry-run: 削除はせず、削除候補一覧と理由だけ表示する。
- 非対話で実行する場合(CI やエージェントから確認なしで削除する場合):
echo y | ./scripts/delete-merged-branches.shで確認プロンプトに自動でyを送る。
スクリプトは (1) merged PR を gh pr list --state merged --base <基準> で一括取得し headRefName / headRefOid で照合する。(2) クローズ済み未マージ PR を gh pr list --state closed(--base なし、リポジトリ全体)から mergedAt == null かつ同一リポジトリ(fork は isCrossRepository で除外)で抽出する。ローカル候補は祖先 or merged PR の headRefOid 一致のみ。リモート専用候補は「merged PR で tip 一致」に加え、クローズ済み未マージ PR については origin の tip が当該 PR の headRefOid と一致する場合のみ(かつ mergedAt == null)削除候補に含め、さらに同名ブランチに open PR がある場合は削除しない。確認後にローカル削除 → リモート削除の順で実行する。
手動で行う場合(フォールバック)
スクリプトを使わないときは以下を参考にする。
-
事前確認
git fetch origin --pruneとgh auth status。主 remote がoriginでない場合はユーザーに確認する。 -
基準ブランチ
ユーザー指定 > ローカルdevelop>origin/HEADの短縮名。base_remote=origin/<基準>が存在しない場合は中断して確認。 -
merged PR の一括取得
ブランチごとにgh pr listを叩かず、1 回だけ取得する。gh pr list --state merged --base "$base_branch" --limit 200 --json headRefName,headRefOid,number -
ローカル候補
git for-each-ref refs/heads --format='%(refname:short)'で一覧。現在ブランチ・基準・main/master/develop・origin/HEAD 先は除外。各ブランチについて:git merge-base --is-ancestor <branch> "$base_remote"で成功 → 削除可(merged by ancestry)- 失敗時は上記 JSON からそのブランチ名の
headRefOidを探し、$(git rev-parse <branch>)と一致する場合のみ削除可(merged PR #N)。
-
リモート専用候補
git for-each-ref refs/remotes/origin --format='%(refname:short)' | sed 's|^origin/||'で一覧。保護ブランチ・ローカルに存在するブランチは除外。(a) merged PR の JSON でそのブランチのheadRefOidが origin の tip と一致する場合。(b) 別途gh pr list --state closed(--base なし)からmergedAt == nullかつ同一リポジトリの PR のheadRefNameとheadRefOidを取得し、origin の tip が headRefOid と一致するリモートブランチのみ対象とする。このとき、同名ブランチに open PR がある場合は削除しない(基準以外向けの sub-PR や Copilot ブランチを含む)。 -
削除
候補を提示し確認後、ローカルはgit branch -d(必要なら-D)、リモートはgit push origin --delete <branch>。
報告形式
基準ブランチ: develop
Deleted (local):
- `feature/foo` - merged by ancestry
- `feature/bar` - merged PR #123 (squash/rebase-safe)
Deleted (remote):
- `feature/qux` - merged PR #124 (remote-only)
- `sub` - closed PR #XXX (remote-only)
削除: ローカル 2 件、リモート 2 件
削除件数を必ず添える。
現状把握(調査時)
リモートブランチ一覧と PR 状態を確認する例:
git fetch origin --prune
git for-each-ref refs/remotes/origin --format='%(refname:short)' | sed 's|^origin/||' | grep -v '^HEAD$' | sort
# 各ブランチの PR 状態: gh pr list --state all --head <branch> --limit 1 --json state,number,mergedAt,baseRefName
削除候補の洗い出しには ./scripts/delete-merged-branches.sh --dry-run が使える。
注意点
- merged PR があっても、ローカルまたはリモートの tip が
headRefOidと一致しない場合は削除しない(merge 後に進んだ可能性あり)。 - クローズ済み未マージ PR のブランチはリモート削除のみ対象。ローカルに同じ名前のブランチがあっても、merged でない限りローカルは削除しない(安全のため)。
- 主 remote が
originでない構成では手順を流用するかユーザーに確認する。 ghが使えない場合は、祖先で merged と判定できるローカルブランチと、その同名のorigin上のリモートブランチを ancestry ベースでのみ削除し、PR のheadRefOidによる squash/rebase 判定およびクローズ済み PR の取得は行わない。