git-branch-cleanup について
git-branch-cleanup は、古い Git ブランチを調べ、マージ済みと確認できたものだけを削除する安全重視のスキルです。squash merge に対応し、未マージの作業があるブランチを保護して、削除確認の前に候補を報告します。
主な機能
- 01ローカルとリモートのブランチを検出
- 02GitHub PR の情報で squash merge を判定
- 03ブランチ内容とマージ後の作業を比較
- 04オープン中、作業中、不明なブランチを保護
- 05確認を求め、復元コマンドを表示
活用シーン
- 01マージ済み PR の後片付け
- 02gone と表示されたブランチの確認
- 03指定ブランチの削除可否を安全に評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dzhuneyt/ skills --skill git-branch-cleanupインストール後、Claude Code で/git-branch-cleanupを入力して読み込まれたか確認します。
詳細
古くなった Git ブランチを削除する
すでにマージ済みのブランチを削除します。作業が残っているブランチはすべて保護してください。
スカッシュマージではコミットが書き換えられます。スカッシュマージ後は、git branch --merged にブランチが表示されず、git branch -d でも削除が拒否されます。そのため、ここではどちらのツールも安全な判断材料になりません。このスキルでは、GitHub にプルリクエストでマージされた内容を問い合わせ、そのコミットとローカルブランチの先端を比較します。
使用するタイミング
次のいずれかに該当する場合は、このスキルを使用します。
- マージ済みのプルリクエストのローカルブランチが残っている。
git branch -vvでブランチに[gone]と表示される。- リポジトリに自動削除の設定がなく、マージ済みのリモートブランチが残っている。
- ユーザーが削除するブランチを1つ以上指定している。
未マージの作業があるブランチの削除には、このスキルを使用しないでください。オープンなプルリクエストを放棄する場合にも使用しないでください。
引数
$ARGUMENTS には、ブランチ名の任意のリストが含まれます。
- リストが空の場合、リポジトリ全体を調べて候補を提示します。
- リストに名前が含まれている場合、その名前のみを評価します。変更を加えずにすべての安全確認を実行します。指定されたブランチも、検出されたブランチと同じ基準で扱います。
ステップ 1:事前確認
まず次のコマンドを実行します。最初のコマンドが失敗した場合は中止してください。
git rev-parse --git-dir # not a repo -> stop
git symbolic-ref --quiet --short refs/remotes/origin/HEAD # -> origin/main
git fetch --prune # every later check needs this
git worktree list --porcelain # branches checked out elsewhere
git remote get-url origin
gh auth status
デフォルトブランチを確認します。main や master という名前だと決めつけないでください。git symbolic-ref で答えが得られない場合は、gh repo view --json defaultBranchRef -q .defaultBranchRef.name を実行します。どちらも失敗した場合は、ユーザーに確認してください。
gh の結果と origin のホストを組み合わせてモードを判定します。origin が GitHub で、gh の認証が有効であればフルモードです。それ以外は制限モードです(「制限モード」を参照)。
ステップ 2:ブランチを一覧表示する
git for-each-ref refs/heads \
--format='%(refname:short)%09%(upstream:short)%09%(upstream:track)%09%(objectname:short)%09%(committerdate:relative)'
git for-each-ref refs/remotes/origin \
--format='%(refname:short)%09%(objectname:short)%09%(committerdate:relative)'
%(upstream:track) は、リモートブランチが存在しなくなると [gone] と表示します。これは、マージ後に削除されたプルリクエストを見つける最も手軽な目印です。API 呼び出しは必要ありません。
ステップ 3:各ブランチのマージ状態を確認する
次のテストを順番に適用します。答えが得られた時点で、それ以降のテストは中止します。
-
git merge-base --is-ancestor <branch> origin/<default>— 終了コードが 0 なら、通常のマージまたはファストフォワードです。ネットワーク接続は必要ありません。 -
gh pr list --head <branch> --state all --limit 5 --json number,state,url,mergedAt,headRefOid- state が
MERGED— ブランチはマージ済みです。これはスカッシュマージのケースです。 - state が
OPEN— ブランチを保護します。絶対に削除しないでください。 - state が
CLOSEDでmergedAtがない — 作業は放棄されています。「放棄」バケットを参照してください。 - プルリクエストがない — テスト 3 に進みます。
- state が
-
マージ結果をデフォルトブランチと比較します。制限モードではこのテストを実行し、フルモードではテスト 2 でプルリクエストが見つからなかった場合にも実行します。
git merge-tree --write-tree origin/<default> <branch> # prints a tree sha git rev-parse origin/<default>^{tree}
ツリーが一致する場合、ブランチに追加内容はありません。そのブランチはマージ済みです。終了コードが 0 以外の場合は競合があるため、ブランチは未マージです。
このテストでは履歴ではなく、内容を読み取ります。別の経路で変更がデフォルトブランチに取り込まれたブランチでも、ツリーは一致します。ツリーが一致する場合、デフォルトブランチにない内容をブランチが保持していないため、結果は安全です。
このテストには git cherry や git branch --merged を絶対に使用しないでください。どちらもコミットを比較するため、スカッシュマージではコミットが書き換えられます。そのため、完全にマージ済みのブランチを未マージとして報告します。git merge-tree は内容を比較するため、スカッシュによってマージが見えなくなることはありません。
git merge-tree --write-tree には git 2.38 以降が必要です。古い git を使用している場合は、削除する前にユーザーに確認してください。
ステップ 4:マージ後に追加されたコミットを見つける
スカッシュマージでは、ブランチ上のすべてのコミットがデフォルトブランチに存在しないように見えます。そのため、
git log origin/<default>..<branch> では、マージ済みの作業と新しい作業を区別できません。プルリクエストが実際にマージしたコミットを GitHub に問い合わせてから、比較します。
git cat-file -e <headRefOid>^{commit} # the sha must exist locally
git log --oneline <headRefOid>..<branch>
git diff --stat <headRefOid>..<branch>
出力がなければ、ブランチに新しい変更はありません。ブランチは安全に削除できます。 出力があれば、プルリクエストに取り込まれなかった作業がブランチに残っています。ブランチをブロックします。
git cat-file が失敗した場合は、git fetch origin <headRefOid> を実行します。それも失敗した場合は、以下の縮退チェックを使用します。
縮退モードでは headRefOid がありません。マージ結果を使います。
git diff --stat origin/<default> $(git merge-tree --write-tree origin/<default> <branch>)
このコマンドは、ブランチに追加され、デフォルトブランチにはないコンテンツを出力します。出力が空なら、ブランチは安全に削除できます。出力があれば、マージされていない作業があるため、ブランチをブロックします。このテストが報告するのはコミットではなくファイルなので、レポートには sha ではなくファイル名を記載してください。
ステップ 5:各ブランチを必ずいずれかの分類に振り分ける
| 分類 | 条件 | 操作 |
|---|---|---|
local-merged | マージ済みで、先端が headRefOid と一致し、現在のブランチでもデフォルトブランチでもなく、ワークツリーにもない | まとめて確認してから削除 |
remote-merged | origin/<x> がマージ済みで、その上に作業を持つローカルブランチがない | まとめて確認してから削除 |
orphan-work | マージ済みだが、headRefOid より後のコミットがある | ブロック |
open-pr | プルリクエストがまだオープン | ブロック |
worktree | 別のワークツリーでチェックアウトされている | ブロック |
system | デフォルトブランチ、または現在のブランチ | ブロック |
abandoned | プルリクエストがマージされずにクローズ | 別途確認 |
stranded | 一度もプッシュされておらず、アップストリームもプルリクエストもない | 報告のみ。削除しない |
unknown | アップストリームがあり、未マージで、プルリクエストがない | 報告のみ |
ブロックされたブランチには削除の確認を求めません。報告して続行します。
ステップ 6:報告してから削除する
確認を求める前に、まず完全なレポートを表示します。各 orphan-work の行には、コミットの sha、件名、経過時間、diffstat を記載します。それ以外の各行は 1 行で記載します。
次に、以下の順序で確認を求めます。
- すべての
local-mergedブランチをまとめて確認します。 - すべての
remote-mergedブランチをまとめて確認します。 abandoned分類について、個別に確認します。プルリクエストの URL とクローズ日を表示します。この確認をステップ 1 またはステップ 2 にまとめてはいけません。
削除する前に、各ブランチの sha を読み取ります。その後、削除します。
git branch -D <branch> # -d refuses squash-merged branches
git push origin --delete <branch>
git branch -D は git のマージチェックを省略します。安全性は代わりにステップ 3 とステップ 4 で確保します。これらのステップで安全と確認されていないブランチに -D を実行してはいけません。
ステップ 7:復元用の控えを出力する
各バッチの後、次の確認を求める前に復元コマンドを出力します。
git branch feat/login-fix a3f9c21
git push origin 8b1e044:refs/heads/feat/export
reflog には期限があります。出力された sha には期限がありません。リモートを復元するには、オブジェクトがローカルリポジトリに存在している必要があります。オブジェクトがない場合は、ユーザーに警告してください。
縮退モード
gh がない、ネットワークが停止している、または origin が GitHub ではない場合、縮退モードを開始します。実行の冒頭で、このことを一度報告します。その後は以下のとおりです。
- スカッシュの検出には
git merge-treeを使います。コンテンツに関しては正確な結果が得られます。 orphan-workのレポートにはコミットではなくファイル名を記載します。abandoned分類は利用できません。クローズ済みのプルリクエストは、プルリクエストがない場合と同じように見えるため、該当するブランチはunknownに振り分けられ、そのまま残ります。- 候補ごとに個別の確認が必要です。まとめて確認することはできません。
縮退モードでは見えないケースが 1 つあります。マージされた作業が後からデフォルトブランチで取り消された場合、git merge-tree はそのブランチを未マージとして報告します。ブランチを報告してください。決して削除してはいけません。
失敗
1 つのブランチで失敗しても、実行を停止しません。失敗を記録して続行します。サマリーには、成功、失敗、スキップしたブランチを記載します。
危険な兆候 — 中止してください
- マージ判定に
git branch --mergedを使用している。同じ理由ですべてのスカッシュマージを検出できません。 - マージ判定に
git cherryを使用している。同じ理由ですべてのスカッシュマージを検出できません。 - 孤立した作業の判定に
git log <default>..<branch>を使用している。マージ済みの作業も新規として報告されます。 - ステップ 3 でクリアされていないブランチに
git branch -Dを実行している。 - レポートの前に削除している。
strandedブランチを削除対象として提示している。- デフォルトブランチ名を検出せずに決めつけている。
よくある間違い
| 間違い | 修正方法 |
|---|---|
git fetch --prune を省略する | マージチェックで古い参照が使われます。最初に実行してください。 |
| 名前付きブランチを事前承認済みとして扱う | 名前でスキャン対象を絞り込みますが、チェックは省略できません。 |
| ローカル側の対応ブランチがまだ先行しているのに、リモートブランチを削除する | ローカルブランチに、その作業の唯一のコピーが残っています。 |
abandoned をマージ済みの一括処理に含める | クローズした作業とマージ済みの作業は、別々に判断する必要があります。 |
| 最後の一括処理の後にのみレシートを表示する | 各一括処理の後に表示してください。後で失敗しても、レシートが隠れないようにします。 |