"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.