Kapitel ▾ 2. Auflage

7.5 Git-Werkzeuge - Suchen

Suchen

Bei nahezu jeder Codebasis-Größe müssen Sie oft herausfinden, wo eine Funktion aufgerufen oder definiert wird, oder den Verlauf einer Methode anzeigen. Git bietet einige nützliche Werkzeuge, um den Code und die in seiner Datenbank gespeicherten Commits schnell und einfach zu durchsuchen. Wir werden einige davon durchgehen.

Git Grep

Git wird mit einem Befehl namens grep ausgeliefert, mit dem Sie problemlos jede committete Baumstruktur, das Arbeitsverzeichnis oder sogar den Index nach einer Zeichenfolge oder einem regulären Ausdruck durchsuchen können. Für die folgenden Beispiele durchsuchen wir den Quellcode von Git selbst.

Standardmäßig durchsucht git grep die Dateien in Ihrem Arbeitsverzeichnis. Als erste Variation können Sie entweder die Optionen -n oder --line-number verwenden, um die Zeilennummern auszugeben, bei denen Git Übereinstimmungen gefunden hat.

$ 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

Zusätzlich zur oben gezeigten Basis-Suche unterstützt git grep eine Fülle weiterer interessanter Optionen.

Anstatt beispielsweise alle Übereinstimmungen auszugeben, können Sie git grep bitten, die Ausgabe zusammenzufassen, indem Sie nur anzeigen, welche Dateien die Suchzeichenfolge enthielten und wie viele Übereinstimmungen es in jeder Datei gab, mit der Option -c oder --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

Wenn Sie am Kontext einer Suchzeichenfolge interessiert sind, können Sie die umschließende Methode oder Funktion für jede übereinstimmende Zeichenfolge mit einer der Optionen -p oder --show-function anzeigen lassen.

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

Wie Sie sehen können, wird die Routine gmtime_r sowohl von den Funktionen match_multi_number als auch match_digit in der Datei date.c aufgerufen (die dritte angezeigte Übereinstimmung stellt lediglich die Zeichenfolge dar, die in einem Kommentar vorkommt).

Sie können auch nach komplexen Zeichenfolgenkombinationen mit dem Flag --and suchen, das sicherstellt, dass mehrere Übereinstimmungen in derselben Textzeile vorkommen müssen. Suchen wir beispielsweise nach Zeilen, die eine Konstante definieren, deren Name entweder die Teilzeichenfolgen "LINK" oder "BUF_MAX" enthält, insbesondere in einer älteren Version der Git-Codebasis, die durch das Tag v1.8.0 repräsentiert wird (wir fügen die Optionen --break und --heading hinzu, die helfen, die Ausgabe in ein besser lesbares Format aufzuteilen).

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

Der Befehl git grep hat einige Vorteile gegenüber normalen Suchbefehlen wie grep und ack. Der erste ist, dass er wirklich schnell ist, der zweite ist, dass Sie jede Baumstruktur in Git durchsuchen können, nicht nur das Arbeitsverzeichnis. Wie wir im obigen Beispiel gesehen haben, haben wir nach Begriffen in einer älteren Version des Git-Quellcodes gesucht, nicht in der Version, die gerade ausgecheckt war.

Git Log Suchen

Vielleicht suchen Sie nicht danach, wo ein Begriff existiert, sondern wann er existierte oder eingeführt wurde. Der Befehl git log bietet eine Reihe leistungsfähiger Werkzeuge, um spezifische Commits anhand des Inhalts ihrer Nachrichten oder sogar des Inhalts des von ihnen eingeführten Diffs zu finden.

Wenn wir beispielsweise herausfinden wollen, wann die Konstante ZLIB_BUF_MAX ursprünglich eingeführt wurde, können wir die Option -S (umgangssprachlich als Git "Pickaxe"-Option bezeichnet) verwenden, um Git anzuweisen, uns nur die Commits anzuzeigen, die die Anzahl der Vorkommen dieser Zeichenfolge geändert haben.

$ 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

Wenn wir uns den Diff dieser Commits ansehen, können wir sehen, dass die Konstante in ef49a7a eingeführt und in e01503b modifiziert wurde.

Wenn Sie spezifischer sein müssen, können Sie einen regulären Ausdruck zur Suche mit der Option -G angeben.

Eine weitere ziemlich fortschrittliche und unglaublich nützliche Log-Suche ist die Zeilenverlaufs-Suche. Führen Sie einfach git log mit der Option -L aus, und es wird Ihnen der Verlauf einer Funktion oder Codezeile in Ihrer Codebasis angezeigt.

Wenn wir beispielsweise jede Änderung sehen wollten, die an der Funktion git_deflate_bound in der Datei zlib.c vorgenommen wurde, könnten wir git log -L :git_deflate_bound:zlib.c ausführen. Dies versucht, die Grenzen dieser Funktion zu ermitteln und durchsucht dann den Verlauf und zeigt uns jede Änderung an, die an der Funktion vorgenommen wurde, als eine Reihe von Patches seit der Erstellung der Funktion.

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

Wenn Git nicht ermitteln kann, wie eine Funktion oder Methode in Ihrer Programmiersprache abgeglichen werden soll, können Sie ihr auch einen regulären Ausdruck (oder Regex) übergeben. Zum Beispiel hätte dies dasselbe bewirkt wie das obige Beispiel: git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c. Sie könnten auch einen Zeilenbereich oder eine einzelne Zeilennummer angeben, und Sie erhalten die gleiche Art von Ausgabe.