世界を動かす技術を、日本語で。

漏洩したCIAの開発者ドキュメントに埋もれていた便利なGitのワンライナーを見つけた

概要

Vault7で公開されたCIAの内部ドキュメントに含まれていた git運用の小技 について解説。 ローカルリポジトリに溜まる 古いブランチの整理方法 が紹介されている。 従来のmasterからmainへの移行にも対応した コマンドの改良版 も説明。 エイリアス化することで 毎回コマンドを覚えずに実行可能。 日々の作業効率化と リポジトリの整理整頓 が可能。

Vault7のgit小技:ローカルの不要ブランチ一括削除

  • 2017年にWikiLeaksが Vault7 として公開したCIAのハッキングツールと内部ドキュメント
  • 内部開発者向けドキュメントに gitの便利な使い方 が記載
  • 時間とともにローカルリポジトリに 不要なブランチが蓄積 する問題
  • git branchコマンドで一覧表示すると 古いブランチが多数残存
  • 標準コマンドgit branch --mergedマージ済みブランチのリストアップ が可能
  • 1つずつ手動削除は 非効率

CIA流・一括クリーンアップコマンド

  • オリジナルコマンド: git branch --merged | grep -v "\*\|master" | xargs -n 1 git branch -d
    • git branch --merged現在のブランチにマージ済みのローカルブランチを一覧
    • grep -v "\*\|master"現在のブランチ(*)とmasterを除外
    • xargs -n 1 git branch -d残りのブランチを1つずつ安全に削除(未マージは削除されない)

main対応・改良版コマンド

  • 多くのプロジェクトがmainを利用する現状に合わせた アップデート版
  • よく使う他のブランチ(例:develop)も 除外指定が可能
  • コマンド例: git branch --merged origin/main | grep -vE "^\s*(\*|main|develop)" | xargs -n 1 git branch -d
    • origin/main基準で マージ済みブランチを抽出
    • grep -vE "^\s*(\*|main|develop)"mainやdevelopも除外
    • xargs残りを一括削除

エイリアス設定と運用

  • 毎回コマンドを覚える必要をなくすため エイリアス化を推奨
  • 例: alias ciaclean='git branch --merged origin/main | grep -vE "^\s*(\*|main|develop)" | xargs -n 1 git branch -d'
  • リポジトリ内でciacleanと打つだけで 不要ブランチの整理完了
  • デプロイ後や作業区切りごとに 定期実行を推奨

効果とまとめ

  • コマンド一発で 40本以上の不要ブランチが一気に数本に整理
  • 毎週数分の時短と 作業環境の清潔化
  • 小さな工夫で 継続的な生産性向上

Hackerたちの意見

今、TUIにハマってるんだ。何かをもっと簡単にしたいときは、claude-codeを開いてTUIをお願いする。今は、gitのワークツリー管理ツールがあって、追加やリベース、削除ができるんだ。TUIライブラリにはTextualを使ってて、claudeが結構うまく扱ってくれる。特に、かなりのPythonコードをテスト実行できるのがいいね。

それ、完全に時間とトークンの無駄に思えるんだけど、何のメリットがあるの?毎回何かするたびに、Claudeに一発でTUIを作らせるってこと?それって計算リソースと君の時間の無駄じゃない?

自分のために作ってる小さなツールの数がすごいことになってるよ。4.6は、今はちゃんと一発か二発でできるみたいで、あんまり気を使わなくても大丈夫そう。これ、オープンソースにしたの?自分も同じこと考えてたんだけど、依存関係の共有についてちょっと考えたかったんだ。例えば、ブランチを試すためにクイックワークツリーをやるとき、npm installとか時間かかるのは避けたいし。もし共有してくれたら、もちろん期待はしないよ。たとえそれが半端な感じのコードでも。

TUIって何か説明してくれない?聞いたことないんだけど。

どんな便利なTUIを作ったの?自分はそのコンセプトに基本的に反対なんだけど。

クロードが書いたコードをどうやって信頼するの?「もしTUIコードにエラーがあったら、私のGitリポジトリがめちゃくちゃになったらどうしよう」って不安にならない?

じゃあ、Tigはいい感じの長くメンテされているGitのTUIだから、楽しめるかもね!少なくともインスピレーションにはなるかも。

Gitの場合、TUIとしてMagitを強くおすすめするよ。頻繁に行う操作が簡単になるだけじゃなくて、あまりやらない操作もできるようになるし、Gitも教えてくれる!私が楽しんでいるMagitの一つの側面についてのドラフトがここにあるよ: https://entropicthoughts.com/rebasing-in-magit

こんなエイリアスを使ってるよ: prune-local = "!git fetch -p && for branch in $(git branch -vv | awk '/: gone]/{if ($1!="*") print $1}'); do git branch -d $branch; done" 1. リモートから最新の情報を取得して、もう存在しないリモートトラッキングブランチを削除 2. ローカルブランチを列挙して、リモートバージョンがもうないとマークされたやつを選択(現在のブランチは無視) 3. ローカルブランチを安全に削除

似たようなことをやってるけど、削除するブランチを選ぶためにfzfを開くんだ。[1] function fcleanb -d "fzfで削除するブランチを選択する" set -l branches_to_delete ( git for-each-ref --sort=committerdate --format='%(refname:lstrip=2) %(upstream:track)' refs/heads/ | \ egrep '[gone]$' | grep -v "master" | \ awk '{print $1}' | $_FZF_BINARY --multi --exit-0 \ ) for branch in $branches_to_delete git branch -D "$branch" end end [1]: https://github.com/jo-m/dotfiles/blob/29d4cab4ba6a18dc44dcf9...

それに似たようなことをやってるよ。ちょっとおしゃれにオプション引数を受け付けたり、一般的な「メインライン」ブランチ名を扱えるようにして、git lintとしてエイリアスを作ってる。[alias] lint = !git branch --merged ${1-} | grep -v -E -e '^[]?[ ](main|master|[0-9]+.-stable)$' -e '^[*][ ]+' | xargs -r -n 1 git branch --delete だから、git pull --prune && git lintは、履歴統計の中でかなり上位にいるよ。

git branch --mergedの主な問題は、リポジトリがスクワッシュマージを強制している場合、当然うまくいかないことだよね。なぜなら、メインのスクワッシュマージされたコミットのSHAは元のブランチのHEADのSHAとは違うから。スクワッシュマージされたブランチを検出するための同等のツールって何が一番いいのかな?注意: この問題は見た目よりも安全に対処するのが難しいんだ。例えば、リモートでスクワッシュマージされたfooというブランチがローカルにあったとして、その前にローカルでいくつかコミットを追加してプッシュし忘れたら、単純にローカルのfooを削除するとデータを失うことになっちゃう。

スクワッシュマージだけじゃなくて、リベースマージも機能しないよ。 > スクワッシュマージされたブランチを検出するための同等のツールって何が一番いいのかな?リモートブランチの削除にフックするのがほとんどの人がやってることだけど、PRのブランチをしばらくしたら整理するって前提だからね。でも、もちろんそれをしないなら機能しないよ。

これはスクワッシュマージリポジトリ用の私のPowerShellバリアントだよ: function Rename-GitBranches { git branch --list "my-branch-prefix/*" | Out-GridView -Title "Zooにするブランチ?" -OutputMode Multiple | % { git branch -m $.Trim() "zoo/$($.Trim())" } } Out-GridViewは、マークを付けたいブランチ名を(複数)選択するための非常にシンプルなダイアログボックスを提供してくれる。私はスクワッシュマージリポジトリでブランチをため込んでいて、ただzoo/というプレフィックスを付けてるんだ。zoo/は一般的にブランチリストの一番下にソートされるし、多くのUIではフォルダとして折りたたむことができる。これがいくつかの点で便利だと感じてるよ。1) スタックされたブランチで作業する時に、git rebase --interactiveがずっと楽になる。--update-refsを利用するからね。マージは共通のベース/祖先を見つけてその作業をしてくれる。スクワッシュマージでは、どのコミットがすでにマージされたかを覚えておく必要がある。--update-refsを使えば、zoo/ブランチを更新しようとしているのを見つけたら、その更新リファレンスの行までのすべてのコミットを削除できるし、その更新リファレンスも削除できる。2) 時々、スクワッシュされたバージョンに入らなかった中間コミットのコードを見つけたいこともある。ブランチのコミットで実験を試みて、その実験を後のコミットで削除した場合、スクワッシュはその削除した実験の証拠をすべて消してしまうけど、zoo/ブランチ名を覚えていればまだ見つけられる。この追加の作業が、マージコミットが無料で簡単にしてくれることを考えると、スクワッシュマージリポジトリがますます嫌いになっちゃう。

あなたのワークフローによると思うよ。あなたが指摘したケースを扱う必要がないし、マージされたらリモートでブランチを削除するからね。だから、上流のブランチがなくなったらローカルブランチを削除するだけで十分だよ。これが私が使っているエイリアスで、HNから拾ったものだよ。# ~/.gitconfig [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 goneを実行するだけ。

fzfと統合したクリーンアップコマンドを持ってるよ。マージされたブランチをすべて事前選択してくれるから、リターンを押すだけで全部削除できるんだ。でも、必要に応じてブランチを保持するために選択解除することもできるよ。リモートブランチも整理してくれる。# マージされたブランチを削除(ローカルとリモート) cleanup = "!git branch -vv | grep ': gone]' | awk '{print $1}' | fzf --multi --sync --bind start:select-all | xargs git branch -D; git remote prune origin;" https://github.com/WickyNilliams/dotfiles/blob/c4154dd9b6980... fzfと統合したいくつかのエイリアスもあって、インタラクティブなチェリーピック(ブランチを選んで、1つ以上のコミットを選ぶ)や、コミットをサイドに表示するプレビュー付きのブランチセレクターもあるよ。めっちゃ便利だね。記事でも、マスターがメインに変わったことが多いけど、いくつかの場所ではdevelopや他の名前をプライマリブランチとして使ってるって言ってるね。だから、そういうブランチを参照するために、いつもGitの設定変数を使ってるよ。私のグローバルGit設定ではメインになってる。必要に応じて各リポジトリのローカル設定でオーバーライドしてるよ。例えば、プライマリを更新して現在のブランチをその上にリベースするコマンドはこんな感じ: # プライマリブランチに切り替えて、プルして、戻ってリベース update = !"git switch ${1:-$(git config user.primaryBranch)}; git pull; git switch -; git rebase -;" https://github.com/WickyNilliams/dotfiles/blob/c4154dd9b6980...

: https://github.com/tj/git-extras/blob/main/Commands.md#git-delete-merged-branches

別のブランチに切り替えずにプルできるよ:git switch my-test-branch ... git pull origin main:main git rebase main

: https://git-scm.com/docs/git-worktree

これが私がgit tidyエイリアスを使ってるワンライナーのやり方だよ[1]。いくつかポイントを挙げると:* デフォルトブランチが削除されないようにする(main、master)* 現在のブランチには触れない* 別のワークツリーのブランチには触れない[1] * マージされていないリポジトリでも機能するように、リモートで消えたローカルブランチを削除する。 git branch --merged "$(git config init.defaultBranch)" \ | grep -Fv "$(git config init.defaultBranch)" \ | grep -vF '' \ | grep -vF '+' \ | xargs git branch -d \ && git fetch \ && git remote prune origin \ && git branch -v \ | grep -F '[gone]' \ | grep -vF '' \ | grep -vF '+' \ | awk '{print $1}' \ | xargs git branch -D

私は全てのリポジトリでmasterを使ってるよ。ずっと前から使ってるし、「あ、やばい、誰かが気を悪くするかもしれないから今回はmainに変えなきゃ」と思ったことは一度もない。プログラミングモードの時は、そんなこと全然考えないからね。今は全部masterだから、もしかしたらmainに変えるのは簡単なgitコマンドかもしれない。でも、何かが微妙に壊れるんじゃないかって不安があって、未知のリスクを受け入れる時間もないんだ。たまたま敏感な開発者が気を悪くしないようにするために、余計に時間がかかるのは嫌だな。

Metaでは、この業界全体での名前変更の大きな推進があったとき、数人の人がほぼ1年をかけてmasterからmainへの名前変更や、ホワイトボックス/ブラックボックスから許可リスト/禁止リストへの変更を管理してた。このことで、彼らは大きな差分数を主張できて、DEIへの大きな貢献を得て、昇進もしたんだ。

それに、gitの「master」ブランチは、マスター録音やマスターコピーにちなんで名付けられてるんだ。複製が作られる元のオリジナルのことだよ。奴隷制と関連付ける人を除いて、これが攻撃的である理由は全くないよ。

これ、git-extrasにすでにあるものに似てるね[1]。