Uso de git bisect

Uso de git bisect


Nuevo
gitproductividaddebug

¿Qué es git bisect?

Git bisect es un comando útil para identificar el commit exacto que introdujo un error. Funciona mediante bisección: vas identificando un commit como bueno o malo y se va dividiendo el historial de commits a la mitad, hasta conseguir finalmente el commit donde empezó a ser visible el error. Así, en pocos pasos delimitas el primer commit defectuoso, optimizando el diagnóstico en proyectos con muchos cambios o difíciles de debugear.

Un ejemplo de este flujo se puede observar en la siguiente animación:

Git bisect

¿Cuándo conviene usar git bisect?

El comando git bisect es particularmente útil cuando te enfrentas a un error y conoces un estado donde el sistema funciona adecuadamente. Conseguir ese punto donde el sistema sí funciona se puede lograr de diversas formas:

  • Tienes alguna rama que quedó atrás con respecto a production y dicha rama tiene un estado correcto de la aplicación.
  • Tienes conocimiento aproximado de la fecha de cuando ocurrió un error, por lo que puedes hacer un checkout a algún commit de esa fecha.
  • Vas probando los commits hacia el pasado, hasta conseguir un estado donde la plataforma siga funcional.

Además de tener identificado el commit con un estado correcto, es preciso tener una forma de probar la situación del sistema. En el caso de que el error sea un error visual es relativamente sencillo evaluarlo porque se puede observar directamente. En el caso donde existe un error en los cálculos, errores en las conexiones con una API, ineficiencias repentinas en el rendimiento del producto; son casos mucho más complicados de verificar, lo que puede volver inviable el uso de este comando.

¿Cómo se utiliza git bisect?

Supón que eres el desarrollador encargado de la creación y mantenimiento del front de la tabla de datos de los usuarios. Bajo ese rol, haces el commit inicial del feature, verificas en tu local, haces deploy en production y luego verificas el correcto funcionamiento en el entorno productivo:

Estado de aplicación correcta

Te comunicas con el resto de tus compañeros y ellos indican que van a hacer varias mejoras a nivel tanto de backend como de frontend en el transcurso de la semana. Pasan 2 o 3 días y al ir a production observas esto:

Estado de aplicación incorrecta

Empiezas a buscar el origen del error rápidamente porque los clientes empiezan a exigir que la tabla no muestra la información correcta. Al revisar git log puedes ver que hubo varios commits desde que lanzaste el feature hasta el último commit en production:

20 test: request spec para CSV export
19 chore: actualizar gems y RuboCop
18 ci: workflow GitHub Actions (lint + test)
17 i18n: mensajes de validación
16 perf: eager-load phone_numbers en index
15 feat: mejora tarjetas de resumen
14 feat: exportación CSV de contactos
13 feat: endpoint GET /contacts/:id
12 docs: README guía de endpoints
11 feat: job para enviar welcome_email
10 feat: agregar campo phone_number a contacts
09 refactor: extraer servicio Contact::Creator
08 fix: validar unicidad de email en Contact
07 feat: ordenar listados por created_at DESC
06 feat: filtros por email y phone
05 feat: endpoint POST /contacts
04 chore: seeds con 10 contactos de prueba
03 feat: añadir ContactsController (JSON) con acción index
02 feat: crear modelo Contact con migración
01 feat: commit inicial tabla contactos

Inmediatamente en esta situación empiezas a buscar la mayor cantidad de evidencia posible:

  • Verificas cada uno de los commits, pero no se te ocurre qué pudo haber pasado.
  • Verificas directamente los archivos de las estadísticas de los usuarios, pero al ser una aplicación tan grande no es tan rápido ni obvio donde ocurrió el error.
  • Le preguntas a tus compañeros, pero dicen que no se adentraron en los archivos de las estadísticas de los usuarios.

En la búsqueda te das cuenta que aún en tu entorno local existe la rama feature/commit-inicial-tabla-contactos con el commit 01 feat: commit inicial tabla contactos, donde ya sabes que el feature funciona perfectamente.

Esta es una buena ocasión para utilizar git bisect, pues tienes delimitado un estado de la aplicación que conoces que está bien, otro estado donde está mal y una forma fiable / rápida de evaluar el funcionamiento de la plataforma:

  1. Como sabes que el 01 feat: commit inicial tabla contactos tiene un estado correcto y el 20 test: request spec para CSV export está incorrecto porque fue el último commit con deploy en production, puedes utilizar estas referencias para hacer el git bisect:
# Commit malo en production
20 test: request spec para CSV export ad37a859df264c74a560afac610ca0ee871a539e
.
.
.
# Commit bueno en local
01 feat: commit inicial 0739275b5f52c223fa552bcdb3953d92de19b21c 
  1. Inicias el git bisect:
% git bisect start
estado: esperando ambos commits buenos y malos
  1. Marcas el commit bueno y el malo, para delimitar el rango de búsqueda:
# 20 test: request spec para CSV export
% git bisect bad ad37a859df264c74a560afac610ca0ee871a539e
estado: esperando commit bueno, commit malo conocido

# 01 feat: commit inicial tabla contactos
% git bisect good 0739275b5f52c223fa552bcdb3953d92de19b21c
Biseccionando: faltan 9 revisiones por probar después de esta (aproximadamente 3 pasos)
[2af32f754ecbc695ca69119814b50b5646120f42] 10 feat: agregar campo phone_number a contacts
  1. Cada vez que observes la aplicación rota, marcarás git bisect bad:

Aplicación con error

  1. Cada vez que observes que la aplicación está bien, marcarás git bisect good:

Aplicación correcta

  1. Así se visualiza todo el procedimiento:
% git bisect good
Biseccionando: faltan 4 revisiones por probar después de esta (aproximadamente 2 pasos)
[88c0975a0f77452a660f7581abade2ebf4020b7b] 15 feat: mejora tarjetas de resumen

% git bisect bad 
Biseccionando: faltan 2 revisiones por probar después de esta (aproximadamente 1 paso)
[81a8f524e0c880bc007c87b1f56408eba88bd333] 12 docs: README – guía de endpoints

% git bisect good
Biseccionando: faltan 0 revisiones por probar después de esta (aproximadamente 1 paso)
[88f2cdb7518d7ac35c3e7b68637ed5c2b02be71b] 14 feat: exportación CSV de contactos

% git bisect good 
88c0975a0f77452a660f7581abade2ebf4020b7b is the first bad commit
commit 88c0975a0f77452a660f7581abade2ebf4020b7b
Author: Edgar Guzmán <xxxxxxx@xxxxx.com>
Date:   Sat May 31 02:01:20 2025 -0400

    15 feat: mejora tarjetas de resumen

 README.md                      |  2 ++
 app/views/users/index.html.erb | 41 +++--------------------------------------
 2 files changed, 5 insertions(+), 38 deletions(-)
  1. Finalmente, consigues el commit culpable, es el commit 15 feat: mejora tarjetas de resumen que justamente modificó el archivo app/views/users/index.html.erb, el cual se encarga de renderizar las estadísticas de los usuarios.

  2. Haces un git bisect reset para devolverte al commit original.

  3. Al conocer el commit donde se ingresó, también puedes conocer el pull request que provee información adicional tal como la tarea / issue asociada, el revisor, comentarios de la revisión, entre otros. Todo esto es útil para buscar la solución correcta para el error ingresado.

  4. Finalmente, hallas una solución y envías un hotfix para que los clientes puedan utilizar la plataforma sin error alguno.

¿Qué consideraciones se deben tener al utilizar git bisect?

  • Como se mencionó en la introducción, si no tienes una forma rápida o replicable de evaluar el estado del sistema para un commit en concreto, el uso de este comando puede volverse inviable al requerir una gran cantidad de esfuerzo para buscar dicho método de evaluación.

  • En muchas circunstancias conseguir el punto donde el estado del sistema es erróneo implica conseguir el error, en esos casos este comando será prescindible.

  • Hay que utilizarlo de forma consciente, pues si se marca erróneamente el estado del sistema con git bisect bad o git bisect good vas a tener un falso positivo lo que puede empeorar el debug para conseguir el error.

Conclusión

El comando git bisect es una herramienta poderosa pues permite obtener de forma rápida y eficiente sin adentrarse a profundidad en el código; sin embargo, solo es útil cuando el estado del sistema es fácilmente de determinar y se tiene un punto claro en la cronología de un estado correcto del sistema. En las circunstancias adecuadas, puede ser la diferencia entre perder tiempo revisando commits o aislar rápidamente el commit culpable de generar el error.