長すぎるブランチ一覧:スプリントを重ねるごとに増える悩みの種
Scrumを採用している5〜10名規模の開発チームでは、2週間のスプリントごとに40〜60件ものPull Request(PR)が作成されることがよくあります。コードレビューを経てmainやdevelopへマージされた後、リポジトリを整理するためにGitHubやGitLabのWeb画面上にあるDelete branchボタンをクリックしてブランチを削除するのが一般的です。
しかし、ローカル環境のターミナルに戻って次のコマンドを実行したときに問題が発覚します。
git branch -r
# または全ブランチを確認:
git branch -a
画面にはorigin/feature/...やorigin/hotfix/...といった大量のブランチがズラリと表示されます。その大半は、実際には3〜6ヶ月も前にGitHub上で削除されたはずのものです。ターミナルでTabキーを押してオートコンプリート(補完)を使おうとするたびに果てしない候補一覧が展開され、目的のブランチを探すのが著しく困難になります。その結果、古いブランチを誤ってcheckoutしてしまうリスクも跳ね上がります。
うっかり同名の古いブランチにcheckoutしてそのまま作業をコミットしてしまえば、revert作業やコンフリクト(conflict)の解消に何時間も浪費することになりかねません。不要なブランチのクリーンアップは、単なる見た目の美しさの問題ではなく、実際のプロジェクト開発において不可欠なリスク回避策なのです。
なぜGitはローカルの古いブランチを自動削除してくれないのか?
この現象は、Gitのブランチ管理における3層構造に起因しています。
- Local branch(ローカルブランチ): 開発者のマシン上に物理的に存在するブランチ(例:
feature/payment)。 - Remote branch(リモートブランチ): GitHubやGitLabのサーバー上に保存されている実際のリモートブランチ(例: リモート上の
payment)。 - Remote-tracking branch(リモート追跡ブランチ): 前回の通信時点におけるリモートブランチの状態を記録したローカル上のコピー参照(例:
origin/feature/payment)。
チームメンバーがGitHub上でブランチを削除すると、(2)は完全に消去されます。しかし、(3)はローカルマシンの隠しディレクトリ.git/refs/remotes/origin/の中にそのまま残ってしまいます。
日常的に使う通常のコマンドでは、この問題は解消されません。
git fetch
git pull
Gitの設計思想における最重要原則は「データの安全性」です。Gitは新しいコミットや参照(ref)をダウンロードして追加するだけであり、ユーザーが明示的に指示しない限り、ローカルにある参照ポインタを勝手に削除することはありません。その結果、サーバー上で既に削除されたブランチが、ローカルマシン上に「ゴーストブランチ(stale branches)」として半永久的に残り続けることになります。
stale branchを一瞬で一括削除する2つの方法
Gitには、サーバー上に存在しなくなったリモート追跡ブランチを比較・削除するためのprune(刈り込み・枝切り)機能が標準で備わっています。状況に合わせて以下の2つの方法から選択できます。
方法1: 専用コマンド git remote prune を実行する
新しいコードを取得(fetch)せずに、不要になったリモート追跡の参照のみを整理したい場合は、次の構文を使用します。
git remote prune <リモート名>
通常、デフォルトのリモート名はoriginです。実際に削除を実行する前に、まずはドライラン(dry-run)オプションを付けて実行し、どのブランチが削除対象になるかを安全にプレビューすることをおすすめします。
git remote prune origin --dry-run
表示されたリストがサーバー上で削除済みのブランチと一致していることを確認したら、本番のクリーンアップを実行します。
git remote prune origin
ターミナルに以下のような詳細な処理結果が出力されます。
Pruning origin
URL: [email protected]:my-team/backend-core.git
* [pruned] origin/feature/oauth2-login
* [pruned] origin/fix/cache-redis-leak
これで不要なリモート追跡ブランチがローカルから綺麗に一掃されます。
方法2: git fetch に –prune フラグを付けて同時に実行する
2つのコマンドを別々に実行する代わりに、最新コードの取得(fetch)と古いブランチの刈り込みを同時に行うようGitに指示できます。それには--pruneフラグ(または短縮形の-p)を付与します。
git fetch --prune
# または短縮コマンド:
git fetch -p
このコマンド1つで、次の2つの処理を並行して実行できます。
- GitHub上の最新コミットおよび新しいブランチをローカルへすべて取得する。
- リモートサーバーの状態とローカルを照合し、サーバー上に既に存在しない
origin/...の追跡ブランチを即座に削除する。
なお、git pullコマンドでも同様のフラグが利用可能です。
git pull --prune
注意すべき違い:リモート追跡ブランチとローカルブランチの違い
ここで注意が必要です。上記のコマンドはいずれもリモート追跡ブランチ(プレフィックスorigin/が付いたもの)のみを削除します。ローカルブランチそのものには一切手を加えません。
開発作業のために事前にローカルへcheckoutしていた場合、そのローカルブランチはそのまま残っています。PRが正常にマージされた後、不要になったローカルブランチを整理するには次のコマンドを実行します。
git branch -d feature/oauth2-login
マージされていないブランチを強制的に完全に削除したい場合は、-dフラグを-Dに変更して実行してください。
根本的な解決策:常時自動 prune を有効化する
毎回のfetch時にわざわざ-pフラグを入力するのは手間がかかります。最もスマートな解決策は、リモートと通信するたびにGitが自動でpruneを行うよう設定しておくことです。
ターミナルで以下のコマンドを1行実行するだけです。
git config --global fetch.prune true
この設定を行えば、以降はgit fetchやgit pullを実行するだけで、バックグラウンドで自動的にブランチの刈り込みが行われるようになります。これにより、ローカルのブランチリストは常にリモートの実際の状態と正確に同期されます。
ローカルマシンの設定を確認するには、次のコマンドを実行します。
git config --global --get fetch.prune
ターミナルにtrueと出力されれば、設定は完了しており正常に動作します。
