- 長年の Git ブランチ整理問題 を解決するためのシンプルなコマンドが、CIA の内部開発文書で見つかった
git branch --merged | grep -v "\*\|master" | xargs -n 1 git branch -d
- このコマンドは
git branch --merged の結果から 現在のブランチと master を除外 し、マージ済みブランチを一括削除する
- 現代のプロジェクトに合わせて、
main と develop ブランチ を除外するよう修正したバージョンもある
- Git alias に登録 して繰り返し作業を自動化でき、単純ながら 継続的な作業効率向上 とリポジトリ整理に役立つツール
Vault7で見つかった Git のヒント
- 2017年に WikiLeaks が公開した Vault7 文書 には、CIA のハッキングツールと内部開発文書が含まれていた
- そのうちの1ページに Git のヒントとテクニック集 があり、内容の大半は一般的なコミット修正、stash、bisect の使い方などだった
- その文書で見つかったワンライナーのコマンドは、今でも私の
~/.zshrc に残っている
古いブランチ整理の問題
- ローカル Git リポジトリでは、時間が経つにつれて マージ済みブランチが蓄積 し、整理が難しくなる
- 機能ブランチ、ホットフィックス、実験用ブランチなどが、マージ後も残って
git branch の一覧を複雑にする
git branch --merged コマンドでマージ済みブランチを確認できるが、手動で削除するのは面倒
CIA 文書の元のコマンド
現代化したコマンドのバージョン
- 大半のプロジェクトが
main ブランチを使っているため、コマンドを次のように修正できる
git branch --merged origin/main | grep -vE "^\s*(\*|main|develop)" | xargs -n 1 git branch -d
- デプロイ後に
main ブランチで実行すると、数十個のブランチが数個まで減る
- このコマンドは Git alias として登録して手軽に実行できる
alias ciaclean='git branch --merged origin/main | grep -vE "^\s*(\*|main|develop)" | xargs -n 1 git branch -d'
- 以後はリポジトリで
ciaclean コマンドを入力するだけで自動整理を実行できる
効率性と実用性
- このコマンドは 毎週数分の時間を節約 し、ブランチ一覧をきれいに保つのに役立つ
- 単純だが 継続的な生産性向上 をもたらす実用的なツールとして評価されている
4件のコメント
HNのコメントに、自分が作ったプログラムを使っている人がいました。
> その用途には
git-trimを使っています:> https://github.com/foriequal0/git-trim
> README には、場合によっては Bash のワンライナーより優れている理由も説明されています。
> https://news.ycombinator.com/item?id=47089533
私も
git goneというエイリアスを設定して使っています。とても便利です- alias.gone = ! git fetch -p && git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == "[gone]" {print $1}' | xargs -r git branch -D
私は純粋な git ではありませんが、
gh-poiというツールを使って整理しています。https://github.com/seachicken/gh-poi
Hacker Newsのコメント
自分は
git tidyという alias を使ってブランチを整理しているデフォルトブランチ(main, master)は削除せず、現在のブランチや他の worktree のブランチにも触れない
リモートで消えたブランチも自動で削除され、コードは自分の dotfiles 設定にある
init.defaultBranchを使うのは危険。リポジトリごとにデフォルトブランチ名が違う可能性があり、この設定はグローバルなので事前に決め打ちする必要がある自分は
git defaultという alias を作って、リモート(origin)から実際のデフォルトブランチを自動検出させている自分は
fzfと統合した cleanup コマンドを使っているマージ済みブランチを事前に選んでまとめて削除でき、必要なら一部を除外することもできる
リモートブランチも一緒に整理でき、コードは自分の .gitconfig 設定にある
また
user.primaryBranch変数を使って、リポジトリごとに異なるデフォルトブランチを指定しているinit.defaultBranchを使ってもいいのではと思う。すでに初期化済みのリポジトリでもgit config --local init.defaultBranch mainと設定すれば動作するgit pull origin main:mainの後にgit rebase mainで処理可能git branch --mergedは squash merge を使うリポジトリでは正しく機能しないsquash されたコミットの SHA が元のブランチの HEAD と異なるため
squash されたブランチを安全に検出できるツールが気になる
完璧ではないが十分実用的で、削除前には必ず確認プロンプトを出す
GitHub のブランチ自動削除設定を参考にしている
たいていはリモートブランチ削除イベントにフックして処理している
git goneという alias を使ってgit fetch -pの後、[gone]状態のブランチを整理するそのため
git branch --merged、git cherry、git log grepの3つの方法を組み合わせたスクリプトを使っているただし、コミットが amend されていたり複数コミットがある場合は誤検出が起こりうる
自分は
git lintという alias でマージ済みブランチを整理しているmain、master、stable ブランチは除外して削除し、
git pull --prune && git lintの組み合わせをよく使うGit コマンド自体は平凡だが、クリックしていたら Wikileaks 由来の文書にたどり着いたのが面白かった
CIA の「Fine Dining」プロジェクトは、USB に隠したマルウェアをアプリのように偽装するツールだった
元の問題は、単に マージされていないブランチの一覧だけを出力することでも解決できる
こういう自然な作業に何行もの bash が必要なのは妙だ
Git のコードベースはこんなに大きいのに、標準機能として提供されていないのが残念
関連ブログ記事も参照
xargsや for loop を少し覚えればこういうのは些細なこと組み込みコマンドにしようとすると、さまざまな例外ケースを扱う必要があり、かえって複雑になるかもしれない
結局、「xargs を覚えたての人みたいだ」という反応もあった
自分も以前、ブログや記事を通じてこういうことを学んだ
自分は最近 TUI 中毒 だ。何か不便があると Claude-code に TUI を作ってくれと頼んでいる
Textual ライブラリを使って Git worktree を管理する TUI を作ったが、Claude は Python コードをかなりうまく扱う
tigという古い Git TUI も勧められた。発想の参考にもなるMagit の rebase 機能に関する記事も参考になる
自分も似たものを Fish shell で実装した
fzfでリモートから消えたブランチを選んで削除する関数だ自分の dotfiles コードにある