Gitで全リモートブランチを横断して文字列を検索する
GitLab のコード検索が使えなかったり、特定の設定値や変数がどのブランチに存在するか調べたいことがあります。
そんなときは git grep を使うと、ブランチを切り替えることなく、複数のリモートブランチを横断して文字列を検索できます。
この記事では、全リモートブランチを対象に文字列を検索する方法を紹介します。
1. まずは最新のブランチ情報を取得する
git fetch --all --prune
-
git fetch:リモートリポジトリから最新の情報を取得する -
--all:登録されているすべてのリモートから取得する -
--prune:リモート側で削除されたブランチに対応する、不要なリモート追跡参照を削除する
この操作では、基本的に現在作業しているローカルブランチや作業ツリーの内容は変更されません。
2. リモートブランチ一覧を確認する
git branch -r
出力例:
origin/HEAD -> origin/main
origin/main
origin/stg
origin/dev
origin/feature/test
origin/HEAD はリモートのデフォルトブランチを指す参照であり、独立したブランチではありません。
3. 全リモートブランチを検索する
まず、基本的な検索コマンドは次のとおりです。
git grep "example-keyword" origin/main origin/stg origin/dev
ただし、これでは検索対象のブランチを手動で指定する必要があります。
すべてのリモート追跡ブランチを自動的に検索対象にするには、次のコマンドを使用します。
git grep -n -F "example-keyword" $(
git for-each-ref \
--format='%(if)%(symref)%(then)%(else)%(refname:short)%(end)' \
refs/remotes
)
出力例:
origin/main:group_vars/main.yml:15:example-keyword
origin/stg:roles/common/defaults/main.yml:8:example-keyword
出力形式は次のとおりです。
ブランチ名:ファイルパス:行番号:一致した行
このコマンドでは、検索対象のブランチを切り替える必要はありません。
また、origin/HEAD のようなシンボリック参照は検索対象から除外しています。
オプションの意味
-
-n:一致した行の行番号を表示する -
-F:検索文字列を正規表現ではなく、固定文字列として扱う
例えば、example.key を検索する場合、-F を指定することで . を任意の1文字ではなく、文字どおりのピリオドとして扱います。
4. コマンドを分解して理解する
① git branch -r
リモート追跡ブランチの一覧を表示します。
git branch -r
出力例:
origin/HEAD -> origin/main
origin/main
origin/stg
origin/dev
ここで表示されるのは、Git がローカルに保持しているリモート追跡ブランチの情報です。
git fetch を実行していない場合、リモートの最新状態と異なる可能性があります。
② --format='%(refname:short)'
ブランチなどの参照名を短い形式で出力します。
git branch -r --format='%(refname:short)'
出力例:
origin
origin/main
origin/stg
origin/dev
通常の git branch -r では、
origin/HEAD -> origin/main
のように表示されます。
しかし、--format='%(refname:short)' を使用した場合、origin/HEAD に相当する参照が origin と表示されることがあります。
そのため、ブランチ名の整形だけでは、検索対象として不要な参照を完全には除外できません。
③ git for-each-ref
Git の参照を一覧表示するコマンドです。
git for-each-ref --format='%(refname:short)' refs/remotes
出力例:
origin
origin/main
origin/stg
origin/dev
refs/remotes を指定することで、リモート追跡ブランチに関係する参照を取得できます。
ただし、このコマンドだけでは origin/HEAD のようなシンボリック参照も含まれます。
そこで、次の書式を指定します。
git for-each-ref \
--format='%(if)%(symref)%(then)%(else)%(refname:short)%(end)' \
refs/remotes
この書式では、参照がシンボリック参照である場合は空文字列を、それ以外の場合は短い参照名を出力します。
出力例:
origin/main
origin/stg
origin/dev
先頭の空行は origin/HEAD に対応するものです。
これにより、検索時に同じブランチを重複して指定することを防げます。
④ $(...)
シェルのコマンド置換です。
$(コマンド)
括弧内のコマンドを先に実行し、その標準出力をコマンドラインに埋め込みます。
例えば、
echo $(git branch -r --format='%(refname:short)')
とすると、次のように出力されます。
origin origin/main origin/stg origin/dev
今回の検索コマンドでは、シンボリック参照を除外したうえで、ブランチ名を git grep の引数に展開します。
つまり、
git grep -n -F "example-keyword" $(
git for-each-ref \
--format='%(if)%(symref)%(then)%(else)%(refname:short)%(end)' \
refs/remotes
)
は、実質的に次のようなコマンドになります。
git grep -n -F "example-keyword" \
origin/main \
origin/stg \
origin/dev
なお、検索対象のリモート追跡ブランチが存在しない場合は、ブランチの指定がなくなり、現在の作業ツリーが検索されることがあります。
⑤ git grep
Git が管理するファイルの内容を検索するコマンドです。
git grep "example-keyword"
この場合、基本的には現在の作業ツリーにある追跡対象ファイルを検索します。
一方、検索対象のブランチを指定すると、そのブランチの最新コミット時点のファイル内容を検索できます。
git grep "example-keyword" origin/main
複数ブランチも指定できます。
git grep "example-keyword" origin/main origin/stg
このように git grep とコマンド置換を組み合わせることで、全リモート追跡ブランチを横断して検索できます。
5. 応用例
大文字・小文字を区別せずに検索する
git grep -niF "example-keyword" origin/main
-i を指定すると、大文字・小文字を区別せずに検索できます。
一致したファイル名だけを表示する
git grep -lF "example-keyword" origin/main
-l を指定すると、一致した行の内容ではなく、ファイル名のみを表示します。
特定のディレクトリだけを検索する
git grep -nF "example-keyword" origin/main -- roles/
-- 以降にパスを指定すると、対象を特定のディレクトリに限定できます。
特定の拡張子のファイルだけを検索する
git grep -nF "example-keyword" origin/main -- '*.yml'
例えば、Ansible の設定ファイルなど、.yml ファイルに限定して検索したい場合に便利です。
6. 注意点
過去のすべてのコミットを検索するわけではない
今回の方法で検索するのは、各リモート追跡ブランチが指すコミット時点のファイル内容です。
過去のコミットに存在し、現在は削除されている文字列は検索対象になりません。
過去の変更履歴を調べたい場合は、次のようなコマンドを利用します。
git log --all -S"example-keyword"
-S は、指定した文字列の出現回数が変化したコミットを探すオプションです。
ファイル内容の横断検索とは目的が異なります。
ローカルブランチは対象外
今回の方法では、refs/remotes 以下のリモート追跡参照を対象としています。
そのため、リモートへ push していないローカルブランチは検索対象に含まれません。
ブランチ数が多い場合は処理時間に注意
検索対象のブランチが多いほど、検索に時間がかかる場合があります。
また、ブランチ数が極端に多い場合は、シェルのコマンドライン引数の長さの制限に達する可能性もあります。
まとめ
git grep を使用すれば、ブランチを切り替えることなく、複数のブランチに存在する文字列を検索できます。
全リモート追跡ブランチを横断検索する場合は、次の組み合わせが便利です。
git fetch --all --prune
git grep -n -F "example-keyword" $(
git for-each-ref \
--format='%(if)%(symref)%(then)%(else)%(refname:short)%(end)' \
refs/remotes
)
特に、Ansible の変数や設定値がどの環境ブランチに定義されているかを調べる場合に役立ちます。
GitLab のコード検索が使えない環境でも、ローカルの Git コマンドだけで調査できるため、覚えておくと便利です。