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

Вы можете передать команде grep и другие интересные опции.

Например, при использовании опции --count Git, в отличие от предыдущего примера, выведет только те файлы, в которых найдены совпадения и их количество в каждом из этих файлов:

$ 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 вызывается из функций match_multi_number и match_digit в файле date.c.

Вы также можете искать сложные комбинации строк, используя опцию --and, которая гарантирует, что будут отображены только строки, имеющие сразу несколько совпадений. Например, давайте в кодовой базе Git версии 1.8.0 найдем строки, в которых присутствует определение константы, содержащее любую из строк “LINK” и “BUF_MAX”.

В этом примере мы также воспользуемся опциями --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 */

Команда git grep имеет несколько преимуществ перед поиском с помощью таких команд, как grep и ack. Во-первых, она действительно быстрая, во-вторых – git grep позволяет искать не только в рабочем каталоге, но и в любом другом дереве Git. Как вы видели, в прошлом примере мы искали в старой версии исходных кодов Git, а не в текущем снимке файлов.

Поиск в журнале изменений Git

Возможно, вы ищете не где присутствует некоторое выражение, а когда оно существовало или было добавлено. Команда git log обладает некоторыми мощными инструментами для поиска определенных коммитов по содержимому их сообщений или содержимому сделанных в них изменений.

Например, если вы хотите найти, когда была добавлена константа ZLIB_BUF_MAX, то вы можете с помощью опции -S попросить Git показывать только те коммиты, в которых была добавлена или удалена эта строка.

$ 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 log передать опцию -L, в результате вам будет показана история изменения функции или строки кода в вашей кодовой базе.

Например, если мы хотим увидеть все изменения, произошедшие с функцией git_deflate_bound в файле zlib.c, мы можем выполнить 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. Также вы можете передать интервал строк или номер определенной строки, и в этом случае вы получите похожий результат.