git-cleanup について
Git Cleanupは、ローカルのGitブランチとworktreeを確認して整理するスキルです。関連ブランチをまとめ、マージ履歴と作業ツリーの状態を調べたうえで、削除前に2段階のユーザー確認を求めます。
主な機能
- 01確認前に関連ブランチをグループ化
- 02マージ済み・作業中・要確認に分類
- 03マージ履歴と追跡状態の確認
- 04worktreeの未コミット変更を検出
- 05削除前の2段階ユーザー確認
活用シーン
- 01増えたローカルブランチの確認
- 02リモート削除後に残った追跡ブランチの調査
- 03確認を経たマージ済みブランチとworktreeの削除
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill git-cleanupインストール後、Claude Code で/git-cleanupを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlassets/trail-of-bits-mark.svg
詳細
Git のクリーンアップ
蓄積された Git のワークツリーとローカルブランチを、安全に分類してクリーンアップします。分類は、安全に削除可能(マージ済み)、関連している可能性あり(類似するテーマ)、作業中(保持)の 3 つです。
使用する場面
- ユーザーのローカルブランチやワークツリーが多数蓄積されている場合
- ブランチがマージ済みなのに、ローカルでクリーンアップされていない場合
- リモートブランチが削除されているのに、ローカルの追跡ブランチが残っている場合
使用しない場面
- リモートブランチの管理には使用しないでください(これはローカルのクリーンアップのみを対象とします)
- gc や prune などのリポジトリ保守タスクには使用しないでください
- ヘッドレスまたは非対話型の自動化には対応していません(破壊的な操作を行う前に、2 つの段階でユーザーの確認が必要です)
基本原則:安全を最優先
ユーザーの明示的な確認なしに、何も削除しないでください。 このスキルでは、破壊的な操作を行う前に各ステップでユーザーの承認を得る、段階的なワークフローを使用します。
重要な実装上の注意点
スカッシュマージされたブランチには強制削除が必要
重要: スカッシュマージされたブランチでは、Git が作業の取り込みを検出できないため、git branch -d は必ず失敗します。これは想定どおりの動作であり、エラーではありません。
ブランチがスカッシュマージ済みだと判断した場合:
- 最初から
git branch -D(強制削除)を使う計画にしてください - 先に
git branch -dを試してから、もう一度-Dの確認を求めないでください。ユーザーに無駄な確認を求めることになります - 確認ステップでは、スカッシュマージされたブランチに
git branch -Dを表示してください
分類する前に、関連するブランチをグループ化する
必須: 個々のブランチを分類する前に、名前のプレフィックスでグループ化してください。
# Extract common prefixes from branch names
# e.g., feature/auth-*, feature/api-*, fix/login-*
同じプレフィックスを持つブランチ(例:feature/api、feature/api-v2、feature/api-refactor)は、ほぼ確実に関連するイテレーションです。グループとして分析してください。
- コミット日時を確認し、最も古いものと最も新しいものを特定する
- 新しいブランチに古いブランチのコミットが含まれているか確認する
- それぞれの作業をどの PR がマージしたか確認する
- 古いブランチが後続のブランチに置き換えられているか判断する
関連するブランチは、カテゴリごとに散らばらせず、明確な推奨事項とともにまとめて提示してください。
PR 履歴を徹底的に調査する
単純なキーワードマッチングに頼らないでください。[gone] ブランチについては:
# 1. Get the branch's commits that aren't in default branch
git log --oneline "$default_branch".."$branch"
# 2. Search default branch for PRs that incorporated this work
# Search by: branch name, commit message keywords, PR numbers
git log --oneline "$default_branch" | grep -iE "(branch-name|keyword|#[0-9]+)"
# 3. For related branch groups, trace which PRs merged which work
git log --oneline "$default_branch" | grep -iE "(#[0-9]+)" | head -20
ワークフロー
フェーズ 1:包括的な分析
分類を行う前に、必要な情報をすべて収集してください。
# Get default branch name
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD \
2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
# Protected branches - never analyze or delete
protected='^(main|master|develop|release/.*)$'
# List all local branches with tracking info
git branch -vv
# List all worktrees
git worktree list
# Fetch and prune to sync remote state
git fetch --prune
# Get merged branches (into default branch)
git branch --merged "$default_branch"
# Get recent PR merge history (squash-merge detection)
git log --oneline "$default_branch" | grep -iE "#[0-9]+" | head -30
# For EACH non-protected branch, get unique commits and sync status
for branch in $(git branch --format='%(refname:short)' \
| grep -vE "$protected"); do
echo "=== $branch ==="
echo "Commits not in $default_branch:"
git log --oneline "$default_branch".."$branch" 2>/dev/null \
| head -5
echo "Commits not pushed to remote:"
git log --oneline "origin/$branch".."$branch" 2>/dev/null \
| head -5 || echo "(no remote tracking)"
done
ブランチ名に関する注意: Git のブランチ名には、シェルの展開を妨げる文字が含まれる場合があります。コマンドでは常に "$branch" のように引用符で囲んでください。
フェーズ 2:関連するブランチをグループ化する
個々のブランチを分類する前に行ってください。
共通のプレフィックスを持つブランチグループを特定してください。
# List branches and extract prefixes
git branch --format='%(refname:short)' | sed 's/-[^-]*$//' | sort | uniq -c | sort -rn
ブランチが 2 つ以上ある各グループについて:
- コミット履歴を比較する — どのブランチに、ほかのブランチのコミットが含まれているか
- マージの証拠を探す — このグループの作業を取り込んだ PR はどれか
- 「最終」ブランチを特定する — 通常は、最も新しいブランチ、または最も完成度の高いブランチです
- 置き換えられたブランチを特定する — 作業が main または新しいブランチに含まれている古いイテレーションです
SUPERSEDED と判断するには、証拠が必要です。プレフィックスの一致だけでは不十分です。
- PR によって作業が main にマージされている、または
- 新しいブランチに古いブランチのコミットがすべて含まれている
- 名前のプレフィックスだけでは不十分です。同じような名前のブランチでも、独立した作業が含まれている場合があります
feature/api-* ブランチの分析例:
### Related Branch Group: feature/api-*
| Branch | Commits | PR Merged | Status |
|--------|---------|-----------|--------|
| feature/api | 12 | #29 (initial API) | Superseded - work in main |
| feature/api-v2 | 8 | #45 (API improvements) | Superseded - work in main |
| feature/api-refactor | 5 | #67 (refactor) | Superseded - work in main |
| feature/api-final | 4 | None found | Superseded by above PRs |
**Recommendation:** All 4 branches can be deleted - work incorporated via PRs #29, #45, #67
フェーズ 3:残りのブランチを分類する
関連グループに含まれないブランチは、個別に分類してください。
Is branch merged into default branch?
├─ YES → SAFE_TO_DELETE (use -d)
└─ NO → Is tracking a remote?
├─ YES → Remote deleted? ([gone])
│ ├─ YES → Was work squash-merged? (check main for PR)
│ │ ├─ YES → SQUASH_MERGED (use -D)
│ │ └─ NO → REMOTE_GONE (needs review)
│ └─ NO → Local ahead of remote? (check: git log origin/<branch>..<branch>)
│ ├─ YES (has output) → UNPUSHED_WORK (keep)
│ └─ NO (empty output) → SYNCED_WITH_REMOTE (keep)
└─ NO → Has unique commits?
├─ YES → LOCAL_WORK (keep)
└─ NO → SAFE_TO_DELETE (use -d)
カテゴリの定義:
| カテゴリ | 意味 | 削除コマンド |
|---|---|---|
| SAFE_TO_DELETE | デフォルトブランチにマージ済み | git branch -d |
| SQUASH_MERGED | スカッシュマージによって作業が取り込まれている | git branch -D |
| SUPERSEDED | グループの一部で、PR による main へのマージ、または新しいブランチへの取り込みが確認済み | git branch -D |
| REMOTE_GONE | リモートで削除されているが、作業が main に見つからない | 要確認 |
| UNPUSHED_WORK | リモートにプッシュされていないコミットがある | 保持 |
| LOCAL_WORK | 固有のコミットがある未追跡ブランチ | 保持 |
| SYNCED_WITH_REMOTE | リモートと同期済み | 保持 |
フェーズ 4:ダーティ状態の検出
すべてのワークツリーと現在のディレクトリに、コミットされていない変更がないか確認します。
# For each worktree path
git -C <worktree-path> status --porcelain
# For current directory
git status --porcelain
警告を目立つように表示します:
WARNING: ../proj-auth has uncommitted changes:
M src/auth.js
?? new-file.txt
These changes will be LOST if you remove this worktree.
ゲート 1:分析結果をすべて提示する
関連するブランチをグループ化し、すべてを 1 つの包括的なビューにまとめて提示します。
## Git Cleanup Analysis
### Related Branch Groups
**Group: feature/api-* (4 branches)**
| Branch | Status | Evidence |
|--------|--------|----------|
| feature/api | Superseded | Work merged in PR #29 |
| feature/api-v2 | Superseded | Work merged in PR #45 |
| feature/api-refactor | Superseded | Work merged in PR #67 |
| feature/api-final | Superseded | Older iteration, diverged |
Recommendation: Delete all 4 (work is in main)
---
### Individual Branches
**Safe to Delete (merged with -d)**
| Branch | Merged Into |
|--------|-------------|
| fix/typo | main |
**Safe to Delete (squash-merged, requires -D)**
| Branch | Merged As |
|--------|-----------|
| feature/login | PR #42 |
**Needs Review ([gone] remotes, no PR found)**
| Branch | Last Commit |
|--------|-------------|
| experiment/old | abc1234 "WIP something" |
**Keep (active work)**
| Branch | Status |
|--------|--------|
| wip/new-feature | 5 unpushed commits |
### Worktrees
| Path | Branch | Status |
|------|--------|--------|
| ../proj-auth | feature/auth | STALE (merged) |
---
**Summary:**
- 4 related branches (feature/api-*) - recommend delete all
- 1 merged branch - safe to delete
- 1 squash-merged branch - safe to delete
- 1 needs review
- 1 to keep
Which would you like to clean up?
明確な選択肢を添えて AskUserQuestion を使用します。
- 推奨対象をすべて削除する(グループ+マージ済み+スカッシュマージ済み)
- 特定のグループ/カテゴリを削除する
- 個別のブランチを選択する
ユーザーが応答するまで先に進まないでください。
ゲート 2:正確なコマンドを使って最終確認する
正しいフラグを指定した、実行予定の正確なコマンドを表示します。
I will execute:
# Merged branches (safe delete)
git branch -d fix/typo
# Squash-merged branches (force delete - work is in main via PRs)
git branch -D feature/login
git branch -D feature/api
git branch -D feature/api-v2
git branch -D feature/api-refactor
git branch -D feature/api-final
# Worktrees
git worktree remove ../proj-auth
Confirm? (yes/no)
重要: 削除に必要な確認はこれだけです。-D が必要な場合でも、確認を追加しないでください。
フェーズ 5:実行する
部分的な失敗によって残りの削除が妨げられないよう、各削除は個別のコマンドとして実行します。それぞれの結果を報告します。
git branch -d fix/typo
git branch -D feature/login
git branch -D feature/api
git branch -D feature/api-v2
git branch -D feature/api-refactor
git branch -D feature/api-final
git worktree remove ../proj-auth
削除に失敗した場合は、エラーを報告して残りの削除を続けます。
フェーズ 6:報告する
## Cleanup Complete
### Deleted
- fix/typo
- feature/login
- feature/api
- feature/api-v2
- feature/api-refactor
- feature/api-final
- Worktree: ../proj-auth
### Remaining (4 branches)
| Branch | Status |
|--------|--------|
| main | current |
| wip/new-feature | active work |
| experiment/old | needs review |
安全ルール
- 自動的に実行しない — ユーザーが明示的に
/git-cleanupを使用した場合にのみ実行する - 確認ゲートは 2 つだけ — 分析の確認、その後に削除の確認
- 正しい削除コマンドを使用する — マージ済みには
-d、スカッシュマージ済み/置き換え済みには-Dを使用する - 保護対象のブランチには決して触れない — main、master、develop、release/*(プログラムで除外済み)
- ダーティなワークツリーの削除をブロックする — データ損失について明示的な了承がない限り拒否する
- 関連するブランチをグループ化する — カテゴリをまたいで分散させない
退けるべき誤った理屈
データ損失につながるよくある近道です。退けてください。
| 誤った理屈 | 間違っている理由 |
|---|---|
| 「ブランチが古いから、削除しても問題ないだろう」 | 古さはマージ状態を示すものではありません。古いブランチにも、マージされていない作業が含まれている可能性があります。 |
| 「必要なら reflog から復元できる」 | reflog のエントリーは期限が過ぎると消えます。reflog の使い方を知らないユーザーも多いため、安全策として頼らないでください。 |
| 「単なるローカルブランチだから、重要ではない」 | ローカルブランチには、どこにもプッシュされていない作業の唯一のコピーが含まれている場合があります。 |
| 「PR がマージされたから、ブランチを削除しても問題ない」 | スカッシュマージではブランチの履歴は保持されません。特定のコミットが取り込まれたことを確認してください。 |
「[gone] のブランチをすべて削除すればいい」 | [gone] はリモートブランチが削除されたことを示すだけです。ローカルブランチに、まだプッシュされていないコミットが含まれている可能性があります。 |
| 「ユーザーはすべて削除したいようだ」 | 必ず最初に分析結果を提示してください。削除対象はユーザーに選んでもらいます。 |
| 「main にないコミットがあるから、未プッシュの作業がある」 | 「main にない」≠「プッシュされていない」。ブランチがリモートと同期済みでも、main にマージされていないことがあります。必ず git log origin/<branch>..<branch> を確認してください。 |