Git
Chapters ▾ 2nd Edition

7.5 Git のさまざまなツール - 検索

検索

コード量の大小を問わず、関数の参照位置・定義やメソッドの変更履歴を確認したくなることはよくあります。Git には便利なツールがいくつも用意されていて、コードやコミット履歴の確認が簡単にできるようになっています。具体的な方法をいくつか見ていきましょう。

Git Grep

Git に付属する grep コマンドを使うと、コミット済みのツリーや作業ディレクトリが簡単に検索(文字列・正規表現)できます。使い方の説明を兼ねて、Git のソースコードを覗いてみることにしましょう。

このコマンドはデフォルトでは作業ディレクトリを検索します。-n オプションと一緒に使うと、検索条件とマッチした行の番号も表示してくれます。

$ git grep -n gmtime_r
compat/gmtime.c:3:#undef gmtime_r
compat/gmtime.c:8:      return git_gmtime_r(timep, &result);
compat/gmtime.c:11:struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
compat/gmtime.c:16:     ret = gmtime_r(timep, result);
compat/mingw.c:606:struct tm *gmtime_r(const time_t *timep, struct tm *result)
compat/mingw.h:162:struct tm *gmtime_r(const time_t *timep, struct tm *result);
date.c:429:             if (gmtime_r(&now, &now_tm))
date.c:492:             if (gmtime_r(&time, tm)) {
git-compat-util.h:721:struct tm *git_gmtime_r(const time_t *, struct tm *);
git-compat-util.h:723:#define gmtime_r git_gmtime_r

その他にも、興味深いオプションがこのコマンドにはいくつも用意されています。

上記の実行例とは違い、コマンド出力を Git に要約させることもできます。例えば、検索にマッチしたファイルの名前とマッチ回数を表示させるには、--count オプションを使います。

$ git grep --count gmtime_r
compat/gmtime.c:4
compat/mingw.c:1
compat/mingw.h:1
date.c:2
git-compat-util.h:2

検索にマッチした結果からメソッドや関数と思われるものだけを確認したい場合は、-p オプションを使いましょう。

$ git grep -p gmtime_r *.c
date.c=static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
date.c:         if (gmtime_r(&now, &now_tm))
date.c=static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
date.c:         if (gmtime_r(&time, tm)) {

この例では、gmtime_r が date.c ファイルにある関数 match_multi_numbermatch_digit から呼び出されていることがわかります。

また、文字列の複雑な組み合わせを探したい場合は --and オプションを使いましょう。検索条件がすべて同一行に含まれている行だけを返すためのオプションです。例として、文字列 “LINK” か “BUF_MAX” を含む定数が記述されている行を、Git の古いバージョン 1.8.0 から探してみます。

なお、この例では --break--heading のオプションも使っています。出力を分割して読みやすくするためです。

$ git grep --break --heading \
    -n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0
v1.8.0:builtin/index-pack.c
62:#define FLAG_LINK (1u<<20)

v1.8.0:cache.h
73:#define S_IFGITLINK  0160000
74:#define S_ISGITLINK(m)       (((m) & S_IFMT) == S_IFGITLINK)

v1.8.0:environment.c
54:#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS

v1.8.0:strbuf.c
326:#define STRBUF_MAXLINK (2*PATH_MAX)

v1.8.0:symlinks.c
53:#define FL_SYMLINK  (1 << 2)

v1.8.0:zlib.c
30:/* #define ZLIB_BUF_MAX ((uInt)-1) */
31:#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */

grepack のような他の検索用コマンドと比較すると、git grep コマンドには利点がふたつあります。とても早く動作することと、作業ディレクトリだけでなくコミット済みの全ツリーが検索対象であることです。上記の例ではその利点を示すために、検索対象を古いバージョンの Git のソースコードとし、チェックアウトされたバージョンのものにはしませんでした。

Git ログの検索

場合によっては、探しているのは語句の 所在 ではなく、語句が存在した・追加された 時期、ということもあるでしょう。git log コマンドの強力なオプションを使うと、コミットメッセージの内容やコミットごとの差分をもとに、特定のコミットを絞り込めます。

ここでは、定数 ZLIB_BUF_MAX が追加された時期を調べてみましょう。その文字列が追加、あるいは削除されたコミットだけを表示するには、-S オプションを用います。

$ git log -SZLIB_BUF_MAX --oneline
e01503b zlib: allow feeding more than 4GB in one go
ef49a7a zlib: zlib can only process 4GB at a time

これらのコミットの差分を見てみると、コミット ef49a7a でこの定数が追加され、コミット e01503b でそれが変更されたことがわかります。

より詳しく調べたいのなら、-G オプションをつけましょう。検索に正規表現が使えるようになります。

ログの行指向検索

一歩進んだログ検索の方法をもうひとつ見ておきましょう。履歴を行指向で検索するという、ものすごく便利な方法です。最近になって Git に追加された機能であまり知られていませんが、本当に便利です。git log コマンドに -L オプションをつけると行指向検索が有効になり、指定した行(関数など)の履歴を確認できます。

ここでは仮に、zlib.c ファイルにある git_deflate_bound 関数の変更履歴を確認したいとしましょう。用いるコマンドは git log -L :git_deflate_bound:zlib.c です。これを実行すると、指定された関数の定義範囲がまずは推測されます。そして、その範囲の全変更履歴をパッチの形でひとつずつ、関数が追加されたときの履歴にまでさかのぼって表示します。

$ git log -L :git_deflate_bound:zlib.c
commit ef49a7a0126d64359c974b4b3b71d7ad42ee3bca
Author: Junio C Hamano <gitster@pobox.com>
Date:   Fri Jun 10 11:52:15 2011 -0700

    zlib: zlib can only process 4GB at a time

diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -85,5 +130,5 @@
-unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
 {
-       return deflateBound(strm, size);
+       return deflateBound(&strm->z, size);
 }


commit 225a6f1068f71723a910e8565db4e252b3ca21fa
Author: Junio C Hamano <gitster@pobox.com>
Date:   Fri Jun 10 11:18:17 2011 -0700

    zlib: wrap deflateBound() too

diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -81,0 +85,5 @@
+unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+{
+       return deflateBound(strm, size);
+}
+

検索対象のコードで用いられているプログラミング言語によっては、 Git が関数やメソッドの定義範囲を絞り込めないことがあります。そんな場合は、正規表現を使いましょう。上記の例でいえば git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c はまったく同じ結果を出力します。また、行番号で検索対象を指定(単一行の指定、複数行で範囲指定の両方が可能)しても、同じような結果が得られます。