gitconfig の alias だけで git grep
の exclude を指定する。
結論
[alias]
grep-with-exclude = !git grep $@ -- ':!*.svg' && :
実際に使っている設定
[alias]
grep-with-exclude = !git grep --line-number -I --show-function --heading --break $@ -- ':!*.svg' ':!*/package-lock.json' ':!*/yarn.lock' ':!*/Gemfile.lock' && :
g = grep-with-exclude --ignore-case
G = grep-with-exclude
# fgrep
f = grep-with-exclude --fixed-strings --ignore-case
F = grep-with-exclude --fixed-strings
# egrep
e = grep-with-exclude --extended-regexp --ignore-case
E = grep-with-exclude --extended-regexp
動機
「確かバージョン 1.0 指定したはずだな…」と思って git grep '1\.0'
とか打つと、大抵 svg ファイルや Gemfile.lock や package-lock.json などから大量に不要な情報がヒットする現象、発生しがちじゃないですか。これ伝わると思う。
けど git grep
に除外パターンを指定するのは結構しんどくて、 git grep '1\.0' -- ':!*.svg' ':!*/package-lock.json' ':!*/yarn.lock' ':!*/Gemfile.lock'
などと打つ必要があり、とにかくダルい。なんとかできないかなとずっと (15年くらい) 思ってたけど、なんとかしたという話。
解説
- gitconfig の alias は
!
で始めて書くとシェルコマンドとして扱われる。 -
git grep
に渡している--
は、シェルにおいてこれ以降はオプションではなく引数として扱ってね、と明示するためのもの。- 例えば
-f
というファイルがあったとして、それを消したいときにrm -- -f
という使い方をする。
- 例えば
-
$@
でgrep-with-exclude
に渡した引数の位置をここに持ってくる。 - このままだとコマンドの一番最後にも引数が渡されてしまうので、全てを無視する
:
というコマンドを&&
で繋げてやる。-
:
というコマンドはシェルスクリプトを書くときにも重宝するやつで、例えばset -e
してる環境で終了コードが 0 以外になるものを取りたいときに&& :
で繋げてやると、スクリプトの終了は抑制しつつ終了コードを取ることができる。
-
実用としては
git grep
のオプションで
-
--line-number
- 行番号を表示。
-
-I
- バイナリファイルの除外。
-
--show-function
- メソッド名とか関数名を出せるらしい。
-
--heading
- 横じゃなくて上にファイル名を表示.
-
--break
- ファイルの境界で空行を出力。
あたりは付けたいので付けておいて、 git g
で git grep
するようにしつつ、大文字小文字の区別や egrep
, fgrep
を使うときの alias まで書くと上のような感じになるかな、というところ。
確認
GIT_TRACE
環境変数に 1 とか true を放り込みながら git
コマンドを実行すると alias がどう解決されて、どう実行されているのかデバッグすることができる。
$ GIT_TRACE=1 git g '1\.0'
17:09:31.602057 git.c:749 trace: exec: git-g '1\.0'
17:09:31.602774 run-command.c:659 trace: run_command: git-g '1\.0'
17:09:31.603284 git.c:407 trace: alias expansion: g => grep-with-exclude
17:09:31.603527 git.c:749 trace: exec: git-grep-with-exclude '1\.0'
17:09:31.603533 run-command.c:659 trace: run_command: git-grep-with-exclude '1\.0'
17:09:31.604084 run-command.c:659 trace: run_command: 'git grep --line-number -I --show-function --heading --break $@ -- '\'':'\!'*.svg'\'' '\'':'\!'*/package-lock.json'\'' '\'':'\!'*/yarn.lock'\'' '\'':'\!'*/Gemfile.lock'\'' && :' '1\.0'
17:09:31.622676 run-command.c:659 trace: run_command: unset GIT_PAGER_IN_USE; LV=-c less
17:09:31.625115 git.c:463 trace: built-in: git grep --line-number -I --show-function --heading --break '1\.0' -- ':'\!'*.svg' ':'\!'*/package-lock.json' ':'\!'*/yarn.lock' ':'\!'*/Gemfile.lock'
こういう感じになり、 git g
に渡した '1\.0'
引数がちゃんと $@
で指定した位置に渡されていて、末尾に渡されるものに関しては && : '1\.0'
となって無事無視されており、期待通りに動いていることが分かる。
ちなみに GIT_TRACE
以外にも GIT_TRACE_SETUP
や GIT_CURL_VERBOSE
環境変数もあるらしい。
締め
前回ブログ書いたのが 1 年前で吃驚した。