Guía

git bisect: cómo encontrar el commit que rompió el código

Actualizado: · Skillaria.Top

git bisect encuentra el commit en el que apareció un error mediante búsqueda binaria en el historial. Ejecute git bisect start, marque el commit malo con git bisect bad y uno que seguro funcionaba con git bisect good <hash>, y luego compruebe los commits que le propone hasta que Git señale al culpable; al final, git bisect reset.

git bisect busca el commit que introdujo un error mediante búsqueda binaria. Usted indica un commit donde el bug existe y otro donde no existía; Git le lleva a la mitad del rango y le pregunta: «¿aquí está bien o mal?». Cada respuesta descarta la mitad de los candidatos, así que entre 1000 commits el culpable aparece en unas 10 comprobaciones.

Es especialmente valioso cuando el error no es evidente: un test falla, el rendimiento cae, la maquetación se descuadra y los diffs no aclaran nada. En lugar de leer cientos de cambios, se hace la misma pregunta sencilla unas cuantas veces.

Cómo funciona la búsqueda binaria en el historial

Supongamos que entre la versión estable v2.3 y el main actual hay 200 commits. Git toma el commit del medio y lo comprueba. Si ahí ya está roto, el error apareció en la primera mitad; si no, en la segunda. El número de candidatos se reduce a la mitad en cada paso: 200, 100, 50, 25, 13, 7, 4, 2, 1. Ocho comprobaciones en lugar de doscientas.

De ahí el requisito principal: la propiedad «está roto» debe ser monótona. Si el bug apareció, desapareció y volvió a aparecer, bisect encontrará una de las transiciones, pero no necesariamente la que usted busca.

git bisect paso a paso: modo manual

1. Localice un commit bueno y uno malo

El malo suele ser el HEAD actual. El bueno, cualquier commit en el que todo funcionaba con seguridad: la etiqueta de la release anterior, un commit de hace una semana. Para encontrar un candidato le ayudarán git log --oneline, git tag o git log --since="2 weeks ago".

2. Inicie la sesión

git bisect start
git bisect bad                 # el commit actual está roto
git bisect good v2.3           # en la release v2.3 todo funcionaba
Bisecting: 99 revisions left to test after this (roughly 7 steps)
[4f5e6d7c...] Actualizar dependencias del frontend

Lo mismo se puede escribir en una línea: git bisect start HEAD v2.3, primero el malo y luego el bueno. Git cambia de inmediato el directorio de trabajo al commit intermedio (en estado detached HEAD).

3. Compruebe y marque

Compile el proyecto, ejecute el test o reproduzca el bug a mano. Después indique el resultado:

git bisect good     # aquí funciona
Bisecting: 49 revisions left to test after this (roughly 6 steps)

git bisect bad      # aquí está roto
Bisecting: 24 revisions left to test after this (roughly 5 steps)

4. Obtenga el resultado

Cuando quede un solo candidato, Git lo mostrará:

a1b2c3d4e5f6... is the first bad commit
commit a1b2c3d4e5f6...
Author: Juan Pérez <juan@example.com>
Date:   Tue Sep 29 11:40:12 2026 +0200

    Cachear el resultado del cálculo del carrito

 src/cart.js | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

5. Cierre la sesión

git bisect reset

El comando le devuelve a la rama desde la que empezó. No se salte este paso: mientras la sesión siga abierta, estará en detached HEAD y es fácil perder commits nuevos. La variante git bisect reset HEAD le deja en el commit encontrado, y git bisect reset <commit>, en el que indique.

Búsqueda automática: git bisect run

Si la comprobación se puede expresar como un comando, deje todo el trabajo a Git. git bisect run ejecuta un script en cada paso y marca el commit según el código de salida:

Código de salidaSignificado
0good: el commit es bueno
1–127, salvo 125bad: el commit es malo
125skip: el commit no se puede comprobar, se omite
mayor que 127 (o <0)abortar bisect (por ejemplo, el script murió por una señal)

Ejemplos de uso:

git bisect start HEAD v2.3
git bisect run npm test
git bisect run pytest tests/test_cart.py -x
git bisect run make check

Para comprobaciones complejas, escriba un pequeño script. Es importante distinguir «no compila» de «el bug se reproduce»; si no, bisect tomará una build rota por el error que busca:

#!/bin/sh
# check.sh
npm ci --silent || exit 125      # no se pudieron instalar las dependencias: omitir
npm run build   || exit 125      # no compila: omitir
node scripts/check-cart-total.js # 0: correcto, 1: bug
chmod +x check.sh
git bisect run ./check.sh

Mantenga el script fuera de los archivos rastreados o en una carpeta que exista en todos los commits que se comprueban: bisect cambia el directorio de trabajo, y un archivo añadido recientemente desaparecerá en los commits antiguos. Lo más fiable es ponerlo en /tmp o en cualquier carpeta fuera del repositorio.

Un test que antes no existía

Situación frecuente: ha escrito un test que reproduce el bug, pero ese archivo no está en los commits antiguos. La solución es la misma copia fuera del repositorio:

cp tests/test_regression.py /tmp/
git bisect run pytest /tmp/test_regression.py

git bisect skip: cuando un commit no se puede comprobar

A veces el commit del medio no compila o falla por otro motivo. No debe marcarlo como good ni como bad, porque falsearía el resultado. Omítalo:

git bisect skip
git bisect skip v2.4..v2.4.3   # omitir un rango

Git elegirá un commit cercano. Si los commits omitidos quedan justo en el límite, al final verá una lista de «posibles culpables» en lugar de uno solo; entonces revise sus diffs a mano.

Términos propios en lugar de good y bad

Bisect no solo sirve para bugs. Por ejemplo, quiere encontrar el commit a partir del cual la página empezó a cargar más rápido. Llamar «malos» a los commits rápidos confunde, así que existen palabras alternativas:

git bisect start --term-old=slow --term-new=fast
git bisect fast HEAD
git bisect slow v2.3

Los sinónimos integrados son old y new: git bisect new, git bisect old. La única regla: el estado «antiguo» debe corresponder al commit más temprano.

Registro de la sesión: log, replay, visualize

  • git bisect log: muestra todas sus marcas. Guárdelo en un archivo si la sesión es larga.
  • git bisect replay archivo: repite la sesión a partir del registro. ¿Se equivocó en el sexto paso? Guarde el log, borre las líneas incorrectas, ejecute git bisect reset y luego replay.
  • git bisect visualize (o git bisect view): muestra los candidatos restantes; con --oneline los lista mediante git log.
git bisect log > /tmp/bisect.log
# editamos el log
git bisect reset
git bisect replay /tmp/bisect.log

Bisect y los commits de merge

En un historial con muchas fusiones, bisect puede llevarle al interior de una rama fusionada: a commits creados antes de que esa rama se sincronizara con main y, por tanto, construidos sobre otra base. Si primero quiere saber qué merge trajo el bug, use:

git bisect start --first-parent

Así Git recorre solo los primeros padres de los commits de merge, es decir, el historial de la rama principal. Una vez encontrado el merge culpable, puede lanzar un segundo bisect dentro de la rama correspondiente.

Errores típicos

  • Confundir good y bad. El resultado no tendrá sentido. Recupere la sesión con git bisect log y replay en lugar de empezar de cero.
  • Directorio de trabajo sucio. Los cambios sin confirmar impiden cambiar de commit y afectan a la comprobación. Antes de empezar, haga un commit o git stash.
  • Artefactos de build sin limpiar. Una caché obsoleta o un node_modules de otro commit dan resultados falsos. Haga una build limpia en el script.
  • Test inestable (flaky). Si el test falla a veces por sí solo, bisect marcará un commit al azar. Ejecute la comprobación varias veces o arregle el test antes de buscar.
  • Olvidar el reset. Se quedó en detached HEAD y empezó a trabajar. El reflog le ayudará a encontrar los commits perdidos.

Ejemplo: búsqueda de una regresión de principio a fin

Los usuarios informan de que el total del carrito se calcula mal cuando hay descuento. En la release v2.3 todo era correcto; en main, no. Escribimos una comprobación mínima y lanzamos la búsqueda:

cat > /tmp/check.sh <<'SH'
#!/bin/sh
npm ci --silent || exit 125
node -e "const c=require('./src/cart'); process.exit(c.total([{price:100}], 0.1) === 90 ? 0 : 1)"
SH
chmod +x /tmp/check.sh

git stash                       # apartar los cambios sin confirmar
git bisect start HEAD v2.3
git bisect run /tmp/check.sh
...
a1b2c3d4e5f6... is the first bad commit
git bisect log > /tmp/cart-bisect.log
git bisect reset
git stash pop

Toda la búsqueda entre un centenar de commits se hace sin su intervención mientras se ocupa de otra cosa. El log guardado le será útil si en la tarea tiene que mostrar a sus compañeros cómo se encontró al culpable.

Qué hacer con el commit encontrado

Cuando tenga al culpable, revíselo completo: git show a1b2c3d. Un commit pequeño con un mensaje claro le indicará la causa enseguida; es una de las razones por las que los equipos valoran los commits atómicos: hacer bisect sobre un historial de enormes «fix stuff» es casi inútil. Después hay dos caminos: corregir el error con un nuevo commit o, si el cambio es fallido en su totalidad y ya está publicado, deshacerlo con git revert a1b2c3d.

Conclusión

git bisect convierte la búsqueda de una regresión, que sería leer cientos de diffs, en unas pocas respuestas de «funciona / no funciona», y con git bisect run, en un único comando. Solo necesita dos referencias —un commit bueno y uno malo— y una comprobación reproducible. Leer el historial, navegar por los commits y buscar errores en él se trabaja en la práctica en el curso de Git.

Preguntas frecuentes

¿Cuántos pasos lleva git bisect?

Aproximadamente log2 del número de commits del rango: unos 7 pasos para 100 commits y unos 10 para 1000. Git muestra la estimación roughly N steps.

¿Se puede usar bisect si no conozco ningún commit bueno?

Hace falta al menos uno. Tome un commit claramente antiguo o una etiqueta de release y compruébelo a mano; los pasos extra por un rango amplio apenas cuestan.

¿git bisect modifica el historial del repositorio?

No, solo cambia el directorio de trabajo entre commits y guarda las marcas en referencias internas refs/bisect, que se eliminan con git bisect reset.

¿Se pueden indicar varios commits buenos?

Sí, a git bisect good se le pueden pasar varios commits; eso estrecha el rango, sobre todo en historiales con ramificaciones.

¿Bisect sirve solo para buscar bugs?

No, permite encontrar cualquier momento en que cambió una propiedad: cuándo apareció un archivo, cuándo se aceleró la build o cuándo cambió un comportamiento. Para eso son útiles los términos old/new o los suyos propios con --term-old y --term-new.

Aprende este tema en vídeo

Este tema se explica a fondo y con práctica en el curso «Git desde cero hasta desarrollador seguro».

Ir al curso