削除済みリモートブランチを追跡しているローカルブランチを1発で全削除するコマンド
リモートブランチはマージ後GitHubが削除するか聞いてくれるのでその時点で削除するようにしている。
だが、そのリモートブランチを追跡しているローカルブランチの削除を忘れて溜まっていることがよくある。
$ git branch -v * develop fef1cdd Merge pull request #30 from abcdef/fix/bazbarfoo feature/foo e57408f [gone] Fix deploy feature/bar a9b73ad [gone] Upload image feature/baz b833113 [gone] Fix test feature/foobar 703e2ad [gone] Add foobar.c feature/foobaz 1587581 [gone] Fix foobaz.rb feature/barfoo 37265cb [gone] Add barfoo.php feature/barbaz 3c121ca [gone] Fix barbaz.py feature/bazfoo cb69767 [gone] Add bazfoo.go feature/bazbar bb8575f [gone] Fix bazbar.pl feature/foobarbaz 66d2807 [gone] Fix README.md fix/foobazbar 84a781e [gone] Add gitignore fix/bazfoobar cd36a6b [gone] Fix ci master 0a41ce8 Merge pull request #20 from abcdef/develop
[gone]
が付いているのがリモートブランチへの追跡が切れているローカルブランチ。
いつも git branch -d
の後ろに1つずつブランチ名をコピペして削除していた。
$ git branch -d feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar Deleted branch feature/foo (was e57408f). Deleted branch feature/bar (was a9b73ad). Deleted branch feature/baz (was b833113). Deleted branch feature/foobar (was 703e2ad). Deleted branch feature/foobaz (was 1587581). Deleted branch feature/barfoo (was 37265cb). Deleted branch feature/barbaz (was 3c121ca). Deleted branch feature/bazfoo (was cb69767). Deleted branch feature/bazbar (was bb8575f). Deleted branch feature/foobarbaz (was 66d2807). Deleted branch fix/foobazbar (was 84a781e). Deleted branch fix/bazfoobar (was cd36a6b).
面倒なので1発コマンド打つだけで全部消えるコマンドを組もうと思い調べた。
コマンド
git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)' | xargs git branch -d
解説
最初は git branch
コマンドを使って作ろうとしていた。
だが、先の記事にも書いた通り、 git branch
を使ってスクリプトを書かないほうがいいらしい。
なので git for-each-ref
を使って書いた。
これはGitレポジトリの各refの情報を条件に従って出力していくコマンド。
Git - git-for-each-ref Documentation
何点か解説を書いてみる。
ブランチ名
ブランチ名は %(refname:short)
で取得。
short
をつけないと refs/heads
がついてしまい、ブランチ名として認識されないので注意が必要。
# `refs/heads` がついてしまう。 $ git for-each-ref --format '%(refname)' refs/heads/develop refs/heads/feature/foo refs/heads/feature/bar refs/heads/feature/baz refs/heads/feature/foobar refs/heads/feature/foobaz refs/heads/feature/barfoo refs/heads/feature/barbaz refs/heads/feature/bazfoo refs/heads/feature/bazbar refs/heads/feature/foobarbaz refs/heads/fix/foobazbar refs/heads/fix/bazfoobar refs/heads/master refs/remotes/origin/HEAD refs/remotes/origin/develop refs/remotes/origin/feature/aaa refs/remotes/origin/feature/bbb refs/remotes/origin/ccc refs/remotes/origin/master refs/remotes/origin/try/ddd refs/remotes/origin/try/eee # shortをつければ正しいブランチ名が取得できる。 $ git for-each-ref --format '%(refname:short)' develop feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar master origin/HEAD origin/develop origin/feature/aaa origin/feature/bbb origin/ccc origin/master origin/try/ddd origin/try/eee
gone
削除対象は [gone]
が付いたブランチなのでこれを取得する。
upstream の項で以下のように説明されている。
:track
also prints "[gone]" whenever unknown upstream ref is encountered.
試すと
$ git for-each-ref --format '%(upstream:track)' [gone] [gone] [gone] [gone] [gone] [gone] [gone] [gone] [gone] [gone] [gone] [gone]
[gone]
のみでは分かりづらいと思うのでブランチ名をつけてみる。
$ git for-each-ref --format '%(upstream:track) %(refname:short)' develop [gone] feature/foo [gone] feature/bar [gone] feature/baz [gone] feature/foobar [gone] feature/foobaz [gone] feature/barfoo [gone] feature/barbaz [gone] feature/bazfoo [gone] feature/bazbar [gone] feature/foobarbaz [gone] fix/foobazbar [gone] fix/bazfoobar master origin/HEAD origin/develop origin/feature/aaa origin/feature/bbb origin/ccc origin/master origin/try/ddd origin/try/eee
リモートへの追跡が切れていない develop
, master
やリモートにのみ存在しているブランチには [gone]
がついておらず、リモートへの追跡が切れたローカルブランチにのみ [gone]
がついている事がわかる。
if
git for-each-ref
ではif文( %(if)…%(then)…%(end)
)が使える。
if文を使い、 [gone]
があるときだけブランチ名を表示するようにする。
$ git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)' feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar
xargs
取得できたブランチ名をxargsを使って git branch -d
に流す。
ちなみにxargsだけ実行すると git branch -d
にどんな値が渡っているのかわかる。
$ git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)' | xargs feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar
こんな感じ。
別解
サブコマンドとして実行してもよい。
git branch -d $(git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)')