Capítulo 26 de 33 8 secciones 9 min

Encontrar en qué momento se rompió

Bisect, que busca por la mitad y resuelve 180 commits en ocho pruebas

<strong><code>git bisect</code> parte el rango por la mitad cada vez.</strong> Le dices un commit donde funcionaba y otro donde ya no, y te va poniendo en el commit del medio para que pruebes. Con marcar <code>good</code> o <code>bad</code> en cada paso, 180 commits se resuelven en ocho pruebas en vez de 180. Y si la prueba se puede automatizar, <code>git bisect run</code> lo hace entero solo.

"El reporte de ventas está dando mal el total y no sé desde cuándo." Esa frase, en un proyecto con meses de historia, suena a tarde perdida 😩

Y se resuelve en cinco minutos, porque Git trae una herramienta que casi nadie usa y que es de las más ingeniosas que tiene.

La idea, con el diccionario

Para buscar "mercadería" en un diccionario no empiezas por la A. Abres por el medio, ves si estás antes o después, y repites. Cada apertura descarta la mitad de lo que queda.

Eso es bisect. Con 180 commits, la primera prueba descarta 90, la segunda 45, y en ocho pruebas ya no queda ninguno. Con mil commits serían diez 🎯

Armar el escenario

Un proyecto donde el total de ventas se rompe en algún punto:

mkdir reporte-ventas
cd reporte-ventas
git init -q
printf 'ciudad,monto\nLima,1200\n' > ventas.csv
git add ventas.csv
git commit -q -m "Primeras ventas de Lima"
printf 'Arequipa,890\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Arequipa"
printf 'Cusco,760\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Cusco"
printf 'Trujillo,mil cuatrocientos\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Trujillo"
printf 'Piura,980\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Piura"
printf 'Chiclayo,1120\n' >> ventas.csv
git add ventas.csv
git commit -q -m "Entran las ventas de Chiclayo"
git log --oneline
dc15797 Entran las ventas de Chiclayo
185a849 Entran las ventas de Piura
c909c26 Entran las ventas de Trujillo
721b81f Entran las ventas de Cusco
508ad4d Entran las ventas de Arequipa
54da6e2 Primeras ventas de Lima

Ahí dentro hay un monto escrito con letras, que es de los errores más comunes cuando el CSV se llena a mano. La prueba de "está bien" va a ser que todos los montos sean números:

cat > revisa.sh <<'FIN'
tail -n +2 ventas.csv | cut -d, -f2 | grep -qv '^[0-9]*$' && exit 1
exit 0
FIN
chmod +x revisa.sh
if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
resultado: 1, esta roto

Devuelve 1, o sea que hoy está roto. Y en el primer commit estaba bien.

Bisect a mano

git bisect start
git bisect bad
git bisect good HEAD~5
status: waiting for both good and bad commits
status: waiting for good commit(s), bad commit known
Bisecting: 2 revisions left to test after this (roughly 1 step)
[721b81f10d1462ae44f0702c6a7fd0a95d0d6238] Entran las ventas de Cusco

Léelo: le dijiste "aquí está mal" y "cinco atrás estaba bien". Git te dejó parada en el commit del medio y te avisa cuántos pasos faltan 🧭

if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
git bisect good
resultado: 0, esta bien
Bisecting: 0 revisions left to test after this (roughly 1 step)
[185a8494d355f2bb7846d3aaaf46475649a746c5] Entran las ventas de Piura
if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
git bisect bad
resultado: 1, esta roto
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[c909c26b9e10ad28185048ed05dbca5f79e095d6] Entran las ventas de Trujillo

Ahí está: el primer commit malo, con su hash, su mensaje y el cambio exacto que lo causó. Tres pruebas 🎉

Salir de bisect

Esto no se puede olvidar, porque mientras bisect está activo estás parada en un commit del pasado:

git bisect reset
git log --oneline -1
git status --short --branch
Previous HEAD position was c909c26 Entran las ventas de Trujillo
Switched to branch 'main'
dc15797 Entran las ventas de Chiclayo
## main
?? revisa.sh

De vuelta donde estabas. reset aquí no borra nada, solo cierra la búsqueda 🔚

Y la versión que se hace sola

Si la prueba se puede escribir como un comando que devuelve 0 cuando está bien, no hace falta que estés delante:

git bisect start HEAD HEAD~5
git bisect run bash revisa.sh
Bisecting: 2 revisions left to test after this (roughly 1 step)
[721b81f10d1462ae44f0702c6a7fd0a95d0d6238] Entran las ventas de Cusco
running 'bash' 'revisa.sh'
Bisecting: 0 revisions left to test after this (roughly 1 step)
[185a8494d355f2bb7846d3aaaf46475649a746c5] Entran las ventas de Piura
running 'bash' 'revisa.sh'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[c909c26b9e10ad28185048ed05dbca5f79e095d6] Entran las ventas de Trujillo
running 'bash' 'revisa.sh'
c909c26b9e10ad28185048ed05dbca5f79e095d6 is the first bad commit
commit c909c26b9e10ad28185048ed05dbca5f79e095d6
Author: Miss Yera <hola@missyera.com>
Date:   Thu Jan 15 09:00:00 2026 -0500

    Entran las ventas de Trujillo

 ventas.csv | 1 +
 1 file changed, 1 insertion(+)
bisect found first bad commit
git bisect reset
git log --oneline -1
Previous HEAD position was c909c26 Entran las ventas de Trujillo
Switched to branch 'main'
dc15797 Entran las ventas de Chiclayo

Eso es lo que hace que bisect valga la pena de verdad: con una prueba automática, encontrar el culpable entre mil commits es escribir dos líneas e ir por un café ☕

La trampa

El reporte de ventas empezó a dar mal el total y nadie sabe desde cuándo. Hay 180 commits desde la última vez que estuvo bien.

$ git log --oneline | wc -l
180

# el plan: revisar uno por uno hacia atras
$ git switch --detach HEAD~1 && python3 reporte.py
$ git switch --detach HEAD~2 && python3 reporte.py
$ git switch --detach HEAD~3 && python3 reporte.py
Qué está mal

A ese ritmo son 180 pruebas y toda la tarde 😩

Buscar hacia atrás de uno en uno es la forma lenta. Git trae la forma rápida: git bisect parte el rango por la mitad cada vez, así que 180 commits se resuelven en ocho pruebas.

Es la misma idea de buscar una palabra en un diccionario: no empiezas por la A, abres por el medio.

Y lo mejor es que no tienes que acordarte de dónde ibas: bisect lleva la cuenta y te va poniendo en el commit que toca probar.

Comprueba que se entendió

Comprueba que lo tienes

Hay 1.000 commits entre "funcionaba" y "está roto". ¿Cuántas pruebas necesita bisect?

  • Unas 10
  • 1.000, una por commit
  • 500, la mitad
  • Depende de dónde esté el commit malo

Ejercicios

1. Arma un proyecto con un error escondido

Seis commits del catálogo de bodegas, y en uno se cuela una ciudad vacía.

cd ..
mkdir catalogo
cd catalogo
git init -q
printf 'bodega,ciudad\nBodega Inti,Cusco\n' > bodegas.csv
git add bodegas.csv
git commit -q -m "Catalogo inicial de bodegas"
printf 'Minimarket Sol,Trujillo\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra el minimarket de Trujillo"
printf 'Bodega Sur,Arequipa\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra la bodega de Arequipa"
printf 'Mayorista Norte,\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra el mayorista del norte"
printf 'Bodega Centro,Lima\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra la bodega del centro"
printf 'Minimarket Sol 2,Piura\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "Entra el segundo minimarket"
git log --oneline
e8b7d18 Entra el segundo minimarket
85a2a3f Entra la bodega del centro
43b47ea Entra el mayorista del norte
2b5abf3 Entra la bodega de Arequipa
4cb519a Entra el minimarket de Trujillo
a9874ae Catalogo inicial de bodegas

Seis commits y una ciudad vacía en alguno.

2. Escribe la prueba

Un script que falla si alguna fila tiene la ciudad vacía.

cat > revisa.sh <<'FIN'
tail -n +2 bodegas.csv | cut -d, -f2 | grep -q '^$' && exit 1
exit 0
FIN
if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
resultado: 1, esta roto

Devuelve 1: hoy está roto.

3. Arranca la búsqueda

Marca dónde está mal y dónde estaba bien.

git bisect start
git bisect bad
git bisect good HEAD~5
status: waiting for both good and bad commits
status: waiting for good commit(s), bad commit known
Bisecting: 2 revisions left to test after this (roughly 1 step)
[2b5abf3e77c6739952fe60654578c14e415456d4] Entra la bodega de Arequipa

Git te deja en el commit del medio y te dice cuántos pasos faltan.

4. Prueba y responde

Ejecuta la prueba en el commit donde estás y marca el resultado.

if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
git bisect good
resultado: 0, esta bien
Bisecting: 0 revisions left to test after this (roughly 1 step)
[85a2a3f408afbceecf92f99bd154287cb65e9a55] Entra la bodega del centro

Bien aquí, así que el culpable está más adelante.

5. Termina la búsqueda

Una prueba más y ya está.

if bash revisa.sh; then echo "resultado: 0, esta bien"; else echo "resultado: 1, esta roto"; fi
git bisect bad
resultado: 1, esta roto
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[43b47ea2dd5b2522239b9db905134b42a2cc0c1c] Entra el mayorista del norte

El primer commit malo, con su hash y su cambio 🎯

6. Hazlo automático

Sal de la búsqueda y vuelve a hacerla sin estar delante.

git bisect reset
git bisect start HEAD HEAD~5
git bisect run bash revisa.sh
git bisect reset
git log --oneline -1
Previous HEAD position was 43b47ea Entra el mayorista del norte
Switched to branch 'main'
Bisecting: 2 revisions left to test after this (roughly 1 step)
[2b5abf3e77c6739952fe60654578c14e415456d4] Entra la bodega de Arequipa
running 'bash' 'revisa.sh'
Bisecting: 0 revisions left to test after this (roughly 1 step)
[85a2a3f408afbceecf92f99bd154287cb65e9a55] Entra la bodega del centro
running 'bash' 'revisa.sh'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[43b47ea2dd5b2522239b9db905134b42a2cc0c1c] Entra el mayorista del norte
running 'bash' 'revisa.sh'
43b47ea2dd5b2522239b9db905134b42a2cc0c1c is the first bad commit
commit 43b47ea2dd5b2522239b9db905134b42a2cc0c1c
Author: Miss Yera <hola@missyera.com>
Date:   Thu Jan 15 09:00:00 2026 -0500

    Entra el mayorista del norte

 bodegas.csv | 1 +
 1 file changed, 1 insertion(+)
bisect found first bad commit
Previous HEAD position was 43b47ea Entra el mayorista del norte
Switched to branch 'main'
e8b7d18 Entra el segundo minimarket

El mismo resultado sin tocar nada en medio ☕

7. Marca sin haber arrancado

Prueba a decir "bad" cuando no hay búsqueda abierta.

git bisect bad
You need to start by "git bisect start"

Git te recuerda que primero hay que arrancar la búsqueda 🛑

Lo que te llevas

Bisect parte por la mitad: mil commits son diez pruebas. Y con un script, ninguna.

¿Tienes alguna duda o consulta?