Git
Chapters ▾ 2nd Edition

7.10 Herramientas de Git - Haciendo debug con Git

Haciendo debug con Git

Git también provee unas cuantas herramientas para realizar un debug a los problemas en tus proyectos. Porque Git está diseñado para trabajar con casi cualquier tipo de proyecto, estas herramientas son bastante genéricas, pero pueden ayudar a cazar bugs o al culpable cuando las cosas salen mal.

Anotaciones de archivo

Si rastreas un bug en tu código y quieres saber cuándo fue introducido y por qué, la anotación de archivo será muchas veces tu mejor herramienta. Esta te muestra qué commit fue el último en modificar cada línea de cualquier archivo. Así que, si ves que un método en tu código tiene bugs, puedes anotar el archivo con git blame para ver cuándo cada línea del método fue editada por última vez y por quién. Este ejemplo usa la opción -L para limitar la salida desde las líneas 12 a 22:

$ git blame -L 12,22 simplegit.rb
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 12)  def show(tree = 'master')
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 13)   command("git show #{tree}")
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 14)  end
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 15)
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 16)  def log(tree = 'master')
79eaf55d (Scott Chacon  2008-04-06 10:15:08 -0700 17)   command("git log #{tree}")
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 18)  end
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 19)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20)  def blame(path)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21)   command("git blame #{path}")
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22)  end

Nota que el primer campo es la parcial de SHA-1 del commit que modificó esa línea. Los siguientes dos campos son valores extraídos del commit- el nombre del autor y la fecha del commit - así podrás ver de manera sencilla quién modificó esa línea y cuándo. Tras estos viene el número de línea y el contenido del archivo. También nota las líneas del commit ^4832fe2, que designan que esas líneas estuvieron en el commit original del archivo. Ese commit es cuando este archivo fue introducido por primera vez al proyecto, y esas líneas no han sido modificadas desde entonces. Esto es un poco confuso, porque ahora has visto al menos tres maneras diferentes en que Git usa el ^ para modificar el SHA-1 de un commit, pero eso es lo que significa aquí.

Otra cosa interesante de Git es que no rastrea los nombres de archivos de forma explícita. Registra las instantáneas y luego intenta averiguar lo que fue renombrado implícitamente, después del hecho. Una de las características interesantes de esto es que se puede preguntar todo tipo de movimiento de código también. Si pasas -C a` git blame`, Git analiza el archivo que estás anotando y trata de averiguar de dónde provienen originalmente fragmentos de código si se copiaron desde otro lugar. Por ejemplo, digamos que estás modificando un archivo llamado GITServerHandler.m en múltiples archivos, uno de estos es GITPackUpload.m. Realizando un blame a GITPackUpload.m con la opción -C, se puede ver de dónde vinieron las secciones del código:

$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 146)         NSString *parentSha;
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 147)         GITCommit *commit = [g
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 149)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151)         if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152)                 [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)

Esto es realmente útil. Normalmente, se obtiene como el commit original aquel de dónde se copió el código, porque esta es la primera vez en la que se tocaron estas líneas en el archivo. Git te informa el commit original donde se escribieron esas líneas, incluso si esto fue hecho en otro archivo.

Anotar un archivo ayuda si sabe dónde está el problema. Si no sabes lo que está mal, y ha habido decenas o cientos de commits desde el último estado en el que sabes que funcionó el código, probablemente te recurrirás a git bisect para obtener ayuda. El comando bisect hace una búsqueda binaria a través de su historial de commits para ayudarle a identificar lo más rápidamente posible qué commit introdujo un problema.

Supongamos que acaba de emitir un release de su código en un entorno de producción, está recibiendo informes de errores sobre algo que no estaba ocurriendo en su entorno de desarrollo y no puede imaginar por qué el código lo está haciendo. Regresa a su código, y resulta que puede reproducir el problema, pero no puede averiguar qué está pasando mal. Puede biseccionar el código para averiguarlo. Primero ejecuta git bisect start para hacer que las cosas funcionen, y luego usas` git bisect bad` para decirle al sistema que el commit actual está roto. Entonces, debe decir a bisect cuándo fue el último estado bueno conocido, usando git bisect good [good_commit]:

$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo

Git se dio cuenta de que se produjeron alrededor de 12 commits entre el commit que marcó como el último commit bueno (v1.0) y la versión mala actual, y se comprobó el del medio para usted. En este punto, puede ejecutar su prueba para ver si el problema existe a partir de este commit. Si lo hace, entonces se introdujo en algún momento antes de este commit medio; Si no lo hace, entonces el problema se introdujo en algún momento después del commit del medio. Resulta que no hay ningún problema aquí, y le dices a Git escribiendo git bisect good y continúa tu viaje:

$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing

Ahora estás en otro commit, a medio camino entre el que acabas de probar y tu mala comisión. Ejecuta la prueba de nuevo y descubre que este commit está roto, por lo que le dices a Git que con git bisect bad:

$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table

Este commit está bien, y ahora Git tiene toda la información que necesita para determinar dónde se introdujo el problema. Le dice que el SHA-1 del primer commit erróneo y muestra algo de la información del commit y qué archivos se modificaron en ese commit para que pueda averiguar qué sucedió que pueda haber introducido este error:

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <pjhyett@example.com>
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config

Cuando haya terminado, debe ejecutar git bisect reset para reiniciar su HEAD a donde estaba antes de comenzar, o terminará en un estado raro:

$ git bisect reset

Esta es una herramienta poderosa que puede ayudarle a comprobar cientos de commits para un bug introducido en cuestión de minutos. De hecho, si tiene un script que retornará 0 si el proyecto está bien u otro número si el proyecto está mal, puede automatizar completamente git bisect. En primer lugar, vuelva a decirle el alcance de la bisectriz, proporcionando los commits malos y buenos. Puede hacerlo enumerándolos con el comando bisect start si lo desea, listando primero el commit malo conocido y el segundo el commit bueno conocido:

$ git bisect start HEAD v1.0
$ git bisect run test-error.sh

Hacerlo ejecuta automáticamente test-error.sh en cada commit de check-out hasta que Git encuentre el primer commit roto. También puede ejecutar algo como make o ` make tests` o lo que sea que ejecute pruebas automatizadas para usted.