Git
Chapters ▾ 2nd Edition

7.5 Εργαλεία του Git - Αναζήτηση

Αναζήτηση

Με κώδικα σχεδόν κάθε μεγέθους, θα χρειαστεί συχνά να εντοπίσουμε πού καλείται ή ορίζεται μια συνάρτηση ή να βρούμε το ιστορικό μιας μεθόδου. Το Git παρέχει μερικά χρήσιμα εργαλεία για γρήγορη και εύκολη αναζήτηση στον κώδικα και τις υποβολές που είναι αποθηκευμένα στη βάση δεδομένων του. Θα δούμε κάποια από αυτά.

Η grep του Git

Το Git συνοδεύεται από μια εντολή που ονομάζεται grep και μας επιτρέπει να αναζητούμε εύκολα μέσα σε οποιοδήποτε υποβεβλημένο δένδρο ή τον κατάλογο εργασίας μια συμβολοσειρά ή κανονική έκφραση (regular expression). Για αυτά τα παραδείγματα, θα εξετάσουμε τον ίδιο τον πηγαίο κώδικα του Git.

Εκ προεπιλογής, θα εξετάσει τα αρχεία στον κατάλογο εργασίας μας. Μπορούμε να περάσουμε την επιλογή -n για να εκτυπώσουμε τους αριθμούς γραμμών όπου το 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: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.

Για παράδειγμα, αντί για την προηγούμενη κλήση, μπορούμε να βάλουμε το Git να συνοψίζει την έξοδο, δείχνοντας ακριβώς σε ποια αρχεία βρέθηκαν αντιστοιχίσεις και πόσες αντιστοιχίσεις βρέθηκαν σε κάθε αρχείο με την επιλογή --count:

$ 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, που διασφαλίζει ότι υπάρχουν πολλαπλές αντιστοιχίσεις βρίσκονται στην ίδια γραμμή. Για παράδειγμα, ας αναζητήσουμε τις γραμμές που ορίζουν μια σταθερά με τις συμβολοσειρές LINK ή BUF_MAX σε αυτές στη βάση του κώδικα του Git σε μια παλαιότερη έκδοση, την 1.8.0.

Εδώ θα χρησιμοποιήσουμε επίσης τις επιλογές --break και --head που βοηθούν στον διαχωρισμό της εξόδου σε πιο ευανάγνωστη μορφή.

$ 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 έχει πολλά ισχυρά εργαλεία για την εξεύρεση συγκεκριμένων υποβολών από το περιεχόμενο των μηνυμάτων τους ή ακόμη και από το περιεχόμενο των diff που εισάγουν.

Αν θέλουμε να μάθουμε, για παράδειγμα, πότε η σταθερά ZLIB_BUF_MAX εισήχθη για πρώτη φορά, μπορούμε να πούμε στο Git να μας δείξει μόνο τις υποβολές που είτε πρόσθεσαν είτε αφαίρεσαν αυτήν τη συμβολοσειρά με την επιλογή -S.

$ 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

Αν κοιτάξουμε το diff αυτών των υποβολών μπορούμε να δούμε ότι η σταθερά εισήχθη στην ef49a7a και τροποποιήθηκε στην e01503b.

Αν χρειάζεται να είμαστε πιο συγκεκριμένοι, μπορούμε να παράσχουμε μια κανονική έκφραση για αναζήτηση με την επιλογή -G.

Γραμμική αναζήτηση στο μητρώο

Μια άλλη αρκετά προηγμένη αναζήτηση στο μητρώο που είναι απίστευτα χρήσιμη είναι η γραμμική αναζήτηση στο ιστορικό. Αυτή είναι μια αρκετά πρόσφατη προσθήκη και δεν είναι πολύ γνωστή, αλλά μπορεί να είναι πραγματικά χρήσιμη. Καλείται με την επιλογή -L στην git log και θα μας δείξει το ιστορικό μιας συνάρτησης ή μιας γραμμής στον κώδικά μας.

Για παράδειγμα, αν θέλαμε να δούμε κάθε αλλαγή που έγινε στη συνάρτηση 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. Θα μπορούσαμε επίσης να του δώσουμε ένα εύρος γραμμών ή έναν μοναδικό αριθμό γραμμής και θα είχαμε το ίδιο είδος εξόδου.