Git
Chapters ▾ 2nd Edition

7.5 Herramientas de Git - Buscando

Buscando

Con casi cualquier tamaño de código de base, a menudo necesitará encontrar en dónde se llama o define una función, o encontrar el historial de un método. Git proporciona un par de herramientas útiles para examinar el código y hacer commit a las almacenadas en su base de datos de forma rápida y fácil. Vamos a revisar algunos de ellos.

Git Grep

Git se envía con un comando llamado grep que le permite buscar fácilmente a través de cualquier árbol o directorio de trabajo con commit por una cadena o expresión regular. Para estos ejemplos, veremos el código fuente de Git.

Por defecto, mirará a través de los archivos en su directorio de trabajo. Puede pasar -n para imprimir los números de línea donde Git ha encontrado coincidencias.

$ 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

Hay una serie de opciones interesantes que puede proporcionar el comando grep.

Por ejemplo, en lugar de la llamada anterior, puede hacer que Git resuma el resultado simplemente mostrándole qué archivos coinciden y cuántas coincidencias hay en cada archivo con la opción --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

Si quiere ver qué método o función piensa que ha encontrado una coincidencia, puede pasar -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)) {

Así que aquí podemos ver que se llama a gmtime_r en las funciones match_multi_number y match_digit en el archivo date.c.

También puede buscar combinaciones complejas de cadenas con el indicador --and, que asegura que múltiples coincidencias estén en la misma línea. Por ejemplo, busquemos cualquier línea que defina una constante con las cadenas “LINK” o “BUF_MAX” en ellas en la base del código de Git en una versión 1.8.0 anterior.

Aquí también usaremos las opciones --break y --heading que ayudan a dividir el resultado en un formato más legible.

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

El comando git grep tiene algunas ventajas sobre los comandos de búsqueda normales como grep y ack. La primera es que es realmente rápido, la segunda es que puedes buscar a través de cualquier árbol en Git, no solo en el directorio de trabajo. Como vimos en el ejemplo anterior, buscamos términos en una versión anterior del código fuente de Git, no en la versión que estaba actualmente verificada.

Búsqueda de Registro de Git

Quizás no está buscando dónde existe un término, sino cuando existió o se introdujo. El comando git log tiene varias herramientas potentes para encontrar commits específicas por el contenido de sus mensajes o incluso el contenido de las diferencias que introducen.

Si queremos saber, por ejemplo, cuándo se introdujo originalmente la constante ZLIB_BUF_MAX, podemos decirle ac Git que solo nos muestre los commits que agregaron o eliminaron esa cadena con la opción -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

Si miramos la diferencia de esos commits, podemos ver que en ef49a7a se introdujo la constante y en e01503b se modificó.

Si necesita ser más específico, puede proporcionar una expresión regular para buscar con la opción -G.

Búsqueda de Registro de Línea

Otra búsqueda de registro bastante avanzada que es increíblemente útil es la búsqueda del historial de línea. Esta es una adición bastante reciente y no muy conocida, pero puede ser realmente útil. Se llama con la opción -L a git log y le mostrará el historial de una función o línea de código en su base de código.

Por ejemplo, si quisiéramos ver cada cambio realizado en la función git_deflate_bound en el archivo zlib.c, podríamos ejecutar git log -L :git_deflate_bound:zlib.c. Esto intentará descubrir cuáles son los límites de esa función y luego examinará el historial y nos mostrará cada cambio que se hizo a la función como una serie de parches de nuevo cuando se creó la función por primera vez.

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

Si Git no puede encontrar la forma de relacionar una función o método en su lenguaje de programación, también puede proporcionarle una expresión regular. Por ejemplo, esto habría hecho lo mismo: git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c. También podría darle un rango de líneas o un único número de línea y obtendrá el mismo tipo de salida.