У базі коду, майже будь-якого розміру, часто потрібно з’ясувати, де функцію викликають, або де вона визначена, або відобразити історію методу. Git пропонує декілька корисних інструментів для швидкого та легкого пошуку в коді та комітах, що були збережені в базі даних Git. Ми розглянемо деякі з них.
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 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 <[email protected]>
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 <[email protected]>
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
.
Ви також можете дати інтервал рядків або один номер рядка, щоб побачити подібний вивід.