Git
Chapters ▾ 2nd Edition

7.5 Інструменти Git - Пошук

Пошук

У базі коду, майже будь-якого розміру, часто потрібно з’ясувати, де функцію викликають, або де вона визначена, або відобразити історію методу. Git пропонує декілька корисних інструментів для швидкого та легкого пошуку в коді та комітах, що були збережені в базі даних Git. Ми розглянемо деякі з них.

Git Grep

Git має команду під назвою grep, що дозволяє легко шукати в будь-якому дереві коміту або робочій теці заданий рядок або за регулярним виразом. У подальших прикладах ми шукатимемо в коді самого Git.

Без додаткових опцій, git grep шукає тільки у файлах вашої робочої директорії. Спершу спробуймо використати опцію -n чи --line-number, щоб вивести номери рядків, в яких Git знайшов збіги:

$ 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:826:struct tm *gmtime_r(const time_t *timep, struct tm *result)
compat/mingw.h:206:struct tm *gmtime_r(const time_t *timep, struct tm *result);
date.c:482:             if (gmtime_r(&now, &now_tm))
date.c:545:             if (gmtime_r(&time, tm)) {
date.c:758:             /* gmtime_r() in match_digit() may have clobbered it */
git-compat-util.h:1138:struct tm *git_gmtime_r(const time_t *, struct tm *);
git-compat-util.h:1140:#define gmtime_r git_gmtime_r

Крім щойно продемонстрованого базового пошуку, команда git grep пропонує безліч інших цікавих опцій.

Наприклад, замість того, щоб виводити всі збіги, можна отримати від git grep підсумок, що показує в яких файлах було знайдено рядок та скільки таких рядків у кожному файлі, за допомогою опції -c чи --count:

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

Якщо вас цікавить контекст навколо шукомого рядка, можна відобразити функцію навколо кожного збігу за допомогою -p чи --show-function:

$ git grep -p gmtime_r *.c
date.c=static int match_multi_number(timestamp_t num, char c, const char *date,
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)) {
date.c=int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
date.c:         /* gmtime_r() in match_digit() may have clobbered it */

Як бачите, процедура gmtime_r викликається з функцій match_multi_number та match_digit у файлі date.c (третій збіг — це просто згадка в коментарі).

Також можна шукати складні комбінації рядків за допомогою опції --and, яка надає можливість шукати декілька збігів, що мають бути в одному рядку тексту. Наприклад, пошукаймо рядки, що визначають константу з назвою, що містить “LINK” або “BUF_MAX”, і що мають бути в старій версії коду Git, яку позначено теґом v1.8.0 (ми також додамо опції --bread та --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, а не тільки в робочій директорії. Як ми бачили в останньому прикладі, ми шукали щось у старіших версіях коду Git, а не в поточній вибраній версії.

Пошук у журналі Git

Напевно вас цікавить не тільки де щось існує, а ще й коли воно існувало або з’явилося. Команда git log пропонує декілька потужних інструментів для пошуку окремих комітів за змістом їх повідомлень або навіть змістом різниці, яку вони додали.

Якщо ви, наприклад, бажаєте дізнатися, коли константа ZLIB_BUF_MAX з’явилася, ви можете використати опцію -S (неформально відома під назвою “кирка” (pickaxe)), щоб попросити Git показати лише коміти, що змінили кількість входжень цього рядка.

$ git log -S ZLIB_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.

Рядковий пошук у журналі

Ще однин доволі складний пошук журналу, що може бути дивовижно корисним — це рядковий пошук історії. Просто використайте опцію -L разом з `git log, і тоді вам буде показана історія функції або рядка коду вашої бази коду.

Наприклад, якщо ми бажаємо побачити кожну зміну функції git_deflate_bount з файлу zlib.c, то ми можемо виконати git log -L :git_deflate_bound:zlib.c. Тоді Git спробує зрозуміти, де межі цієї функції та буде проглядати історію, ті покаже нам кожну зміну, що була зроблена в цій функції у вигляді послідовності патчів аж до моменту створення цієї функції.

$ 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. Ви також можете дати інтервал рядків або один номер рядка, щоб побачити подібний вивід.