La suite de pruebas lleva tres semanas en verde. Hoy npm test falla en una aserción que no tiene nada que ver con lo que acabas de cambiar, y git log v1.4.0..HEAD muestra 340 commits de cuatro personas desde la última release que alguien confirmó que funcionaba. Leer cada diff te cuesta un día entero. Hacer checkout de commits al azar es más rápido, pero sigue sin ser un plan.

git bisect convierte eso en una búsqueda binaria. Le das un commit que sabes que es bueno y otro que sabes que está roto; hace checkout del commit a mitad de camino entre ambos y espera a que pruebes ese commit y reportes el resultado. Cada respuesta reduce el rango a la mitad. 340 commits se resuelven en un máximo de 9 pruebas, porque 2^9 = 512.

Cómo git bisect reduce la búsqueda a la mitad en cada paso

Imagina los 340 commits en una línea, del más antiguo al más nuevo, el tag bueno conocido en un extremo y el HEAD roto en el otro. git bisect no recorre esa línea. Salta al medio, espera un veredicto y luego descarta la mitad que ese veredicto descarta:

1
2
3
4
good                                          bad
 |------------------------|------------------|
                    ^
              prueba este

Supongamos que el commit del medio pasa. El bug está en la mitad más reciente, así que la mitad más antigua desaparece para siempre y en esta sesión no volverás a ver esos commits. Repite: mitad de lo que queda, prueba, descarta la mitad. Tras aproximadamente log2(n) rondas, el rango se reduce a un solo commit — aquel en el que el comportamiento pasó de funcionar a fallar. No siempre es el commit que “causó” el bug en un sentido más profundo. Es el commit donde el comportamiento cambió, que casi siempre es justo lo que necesitas.

Cómo iniciar una sesión de git bisect

Ejecuta esto desde un árbol de trabajo limpio. Bisect hace checkout de commits por ti, y se niega a arrancar si hay cambios sin confirmar.

1
2
3
git bisect start
git bisect bad HEAD
git bisect good v1.4.0

Git hace checkout del punto medio y te dice cuánto queda:

1
2
Bisecting: 169 revisions left to test after this (roughly 8 steps)
[a1b2c3d4e5f6...] Refactor payment retry logic

Ejecuta tu prueba en ese commit y luego reporta lo que encontraste:

1
2
npm test
git bisect bad     # la prueba falló aquí

o bien

1
git bisect good    # la prueba pasó aquí

Git hace checkout del nuevo punto medio automáticamente después de cada respuesta. Sigue probando y reportando hasta que ya no quede nada que reducir:

1
2
3
4
5
6
a1b2c3d4e5f6789... is the first bad commit
commit a1b2c3d4e5f6789...
Author: Priya Nair <[email protected]>
Date:   Tue Sep 2 11:14:02 2026 +0000

    Refactor payment retry logic

Esa es tu respuesta. Lee ese diff, no los otros ocho commits que lo rodean. git bisect reset te devuelve al branch y al commit desde donde empezaste, y los checkouts que hizo bisect por el camino no dejan rastro.

1
git bisect reset

Automatizar el ciclo con git bisect run

Responder good o bad a mano está bien para un bug que detectas en unos segundos. Para algo que necesita una ejecución de pruebas real, delega todo el ciclo a un script:

1
2
3
4
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./bisect-test.sh

bisect-test.sh solo tiene que salir con el código correcto: 0 significa bueno, cualquier código del 1 al 127 salvo 125 significa roto, y 125 significa “no puedo probar este commit, sáltalo”.

1
2
#!/bin/sh
npm test

La mayoría de los test runners ya salen con 0 cuando pasan y con un código distinto de cero cuando fallan, así que a menudo el script se reduce a llamar al runner. git bisect run hace checkout de cada punto medio, ejecuta el script, lee el código de salida y reporta el resultado él mismo, imprimiendo al final el mismo resumen de “first bad commit”.

El script puede hacer más que llamar a un test runner. Si la regresión solo se ve en un artefacto construido, un script que ejecuta docker build y luego pone a prueba la imagen es algo normal para pasarle a git bisect run — los pasos de build de Construir y Publicar una Imagen Docker con GitHub Actions funcionan en un script tan bien como en un workflow. Cuenta con ello: cada ronda ahora cuesta una build completa de la imagen.

Hay una condición para el propio script. Mantenlo fuera del rango sobre el que haces bisect, o asegúrate de que no ha cambiado dentro de ese rango. Si el script vive en la historia que estás buscando, un commit antiguo hace checkout de una versión antigua del script junto con el código antiguo, y dejas de ejecutar la misma prueba en cada paso.

Saltar commits que no puedes probar con git bisect skip

Algunos commits no se pueden probar por sí solos: un commit a medio refactorizar que no compila, una migración de esquema que necesita un estado de base de datos que no tienes. Forzar un veredicto good/bad en uno de esos envenena el resultado. Exclúyelo en su lugar:

1
git bisect skip

Bisect hace checkout de un commit cercano y sigue rodeando el hueco. En bisect run, el código de salida 125 hace lo mismo automáticamente, así que un script capaz de detectar “este commit ni siquiera compila” puede saltarlo en vez de reportar un falso bad. Si saltas demasiados commits adyacentes, bisect se queda sin terreno donde probar: la respuesta final se convierte en “el bug viene de uno de estos N commits” en vez de un solo commit.

Cuándo git bisect no es la herramienta adecuada

Bisect asume que tu prueba es determinista: mismo commit, mismo resultado, siempre. Una prueba inestable rompe esa suposición sin avisar. Bisect no puede distinguir “este commit está roto” de “esta prueba falló por inestabilidad”, y una respuesta equivocada al principio manda cada ronda posterior a la mitad equivocada. Arregla o excluye la aserción inestable antes de hacer bisect, no después.

También asume que un commit produjo un único cambio de comportamiento limpio. Un bug que se coló a lo largo de varios commits, o uno que depende de un estado acumulado en vez de un solo diff, no converge en nada útil. Un historial con muchos rebases es una versión más leve del mismo problema (mira Git Rebase vs Merge: diferencias y cuándo usarlos): el primer commit malo que encuentra bisect puede ser un commit aplastado que agrupa cinco cambios lógicos. Correcto, pero demasiado grueso para revisarlo por separado.

Ninguno de los dos casos pide una herramienta distinta, solo un punto de partida distinto. Primero haz que la prueba sea fiable, luego elige un commit good lo bastante atrás como para estar seguro de que el bug todavía no estaba ahí.

La próxima vez que aparezca una regresión sin causa evidente, marca los dos extremos y responde good o bad unas cuantas veces — o escribe el script de tres líneas y deja que git bisect run responda por ti.

Artículos relacionados