Git
Chapters ▾ 2nd Edition

7.5 Git Tools - Zoeken

Zoeken

Met zo ongeveer elke formaat codebase, zal je vaak de behoeft hebben om uit te zoeken waar een functie wordt aangeroepen of gedefiniëerd, of de historie van een methode uitzoeken. Git heeft een aantal handige instrumenten om snel en eenvoudig door de code en commits die in de database staan te zoeken. We zullen er hier een aantal behandelen.

Git Grep

Git wordt geleverd met een commando genaamd grep wat je in staat stelt om eenvoudig door elke gecommitte boomstructuur (tree) of de werk directory te zoeken naar een woord of reguliere expressie (regular expression). Voor de volgende voorbeelden zoeken we door de broncode van Git zelf.

Standaard zal het door de bestanden in je werk directory zoeken. Je kunt -n doorgeven om de regelnummers af te drukken waar Git resultaten heeft gevonden.

$ 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

Er zijn een aantal interessante opties die je bij het grep commando kunt gebruiken.

Bijvoorbeeld: in plaats van de vorige aanroep, kan je Git de uitvoer laten samenvatten door het alleen te laten zien in welke bestanden resultaten zijn gevonden en hoeveel resultaten er in elk bestand gevonden zijn met de --count optie:

$ 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

Als wilt zien in welke methode of functie het denkt een resultaat te hebben gevonden, kan je -p doorgeven:

$ 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)) {

Hier kunnen we dus zien dat gmtime_r wordt aangeroepen in de match_multi_number en match_digit functies in het bestand date.c.

Je kunt ook zoeken naar ingewikkelde combinaties van woorden met de --and vlag, die ervoor zorgt dat er meerdere treffers zijn op dezelfde regel. Bijvoorbeeld, laten we kijken of er regels zijn die een constante definiëren met het woord "LINK" of "BUF_MAX" in de Git codebase in een oudere 1.8.0 versie.

Hier zullen we ook gebruik maken van de --break en --heading opties die ervoor zorgen dat de uitvoer in een meer leesbaar formaat worden weergegeven.

$ 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 */

Het git grep commando heeft een aantal voordelen boven reguliere zoek commando’s zoals grep en ack. De eerste is dat het erg snel is, de tweede is dat je door elke tree in Git kunt zoeken, niet alleen de werk directory. Zoals we zagen in het bovenstaande voorbeeld, zochten we naar termen in een oudere vesie van de Git broncode, niet de versie die op dat moment uitgechecked was.

Zoeken in de Git log

Misschien ben je niet op zoek naar waar een term bestaat, maar wanneer het bestond of was geïntroduceerd. Het git log commando heeft een aantal krachtige instrumenten om specifieke commits te vinden gebruik makende van hun berichten of zelfs de inhoud van de diff die erdoor werd geïntroduceerd.

Als we bijvoorbeeld willen uitzoeken wanneer de constante ZLIB_BUF_MAX voor het eerst werd geïntroduceerd, kunnen we Git vragen ons alleen de commits te tonen waarin dit woord werd toegevoegd of verwijderd met de -S optie.

$ 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

Als we naar de diff van deze commits kijken kunnen we zien dat in ef49a7a deze constante werd geïntroduceerd en in e01503b werd gewijzigd.

Als je nog specifieker moet zijn, kan je een regular expression opgeven om mee te zoeken met de -G optie.

Zoeken in de regel-log

Een andere nogal gevorderde log zoekmethode die ongelofelijk nuttig is, is de regel historie zoekmethode. Dit is een redelijk nieuwe toevoeging en niet erg bekend, maar het kan erg nuttig zijn. Het wordt aangeroepen met de -L optie bij git log en zal je de historie van een functie of een regel code in je code base tonen.

Bijvoorbeeld, als we elke wijziging willen zien die gemaakt is aan de functie git_deflate_bound in het bestand zlib.c, kunnen we git log -L :git_deflate_bound:zlib.c aanroepen. Dit zal proberen uit te vinden wat het begin en einde is van die functie en dan door de historie gaan en ons elke wijziging laten zien die aan deze functie gemaakt is als een reeks van patches tot waar de functie als eerste gemaakt was.

$ 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);
+}
+

Als Git niet kan uitvinden hoe een functie of methode in jouw programmeertaal kan worden gevonden, kan je het ook een regex meegeven. Bijvoorbeeld, dit zou tot hetzelfde resultaat geleid hebben: git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c. Je kunt het ook een aantal regels als grens meegeven of een enkele regelnummer en je zult een vergelijkbare uitvoer krijgen.