git bisect: como encontrar o commit que quebrou o código
O git bisect encontra o commit em que um bug apareceu fazendo uma busca binária no histórico. Rode git bisect start, marque o commit ruim com git bisect bad e um que com certeza funcionava com git bisect good <hash>, depois teste os commits sugeridos até o Git apontar o culpado; no fim, git bisect reset.
O git bisect procura o commit que introduziu um erro usando busca binária. Você indica um commit em que o bug existe e outro em que ele não existia — o Git leva você para o meio do intervalo e pergunta: «aqui está bom ou ruim?». Cada resposta elimina metade dos candidatos, por isso entre 1000 commits o culpado aparece em umas 10 verificações.
Isso vale ouro quando o erro não é óbvio: um teste falhou, a performance caiu, o layout quebrou e pelos diffs não dá para entender nada. Em vez de ler centenas de alterações, você responde à mesma pergunta simples algumas vezes.
Como funciona a busca binária no histórico
Suponha que entre a versão estável v2.3 e o main atual haja 200 commits. O Git pega o commit do meio e você o testa. Se ali já está quebrado, o erro surgiu na primeira metade; se não, na segunda. O número de candidatos cai pela metade a cada passo: 200, 100, 50, 25, 13, 7, 4, 2, 1. Oito verificações em vez de duzentas.
Daí vem o requisito principal: a propriedade «quebrado» precisa ser monotônica. Se o bug apareceu, sumiu e voltou, o bisect vai achar uma das transições, mas não necessariamente a que você quer.
git bisect passo a passo: modo manual
1. Encontre um commit bom e um ruim
O ruim costuma ser o HEAD atual. O bom é qualquer commit em que tudo com certeza funcionava: a tag do último release, um commit de uma semana atrás. Para achar um candidato, use git log --oneline, git tag ou git log --since="2 weeks ago".
2. Inicie a sessão
git bisect start
git bisect bad # o commit atual está quebrado
git bisect good v2.3 # no release v2.3 tudo funcionava
Bisecting: 99 revisions left to test after this (roughly 7 steps)
[4f5e6d7c...] Atualizar dependências do frontend
Dá para escrever o mesmo em uma linha: git bisect start HEAD v2.3 — primeiro o ruim, depois o bom. O Git muda o diretório de trabalho na hora para o commit do meio (em estado de detached HEAD).
3. Teste e marque
Faça o build do projeto, rode o teste ou reproduza o bug manualmente. Depois informe o resultado:
git bisect good # aqui funciona
Bisecting: 49 revisions left to test after this (roughly 6 steps)
git bisect bad # aqui está quebrado
Bisecting: 24 revisions left to test after this (roughly 5 steps)
4. Veja o resultado
Quando sobrar um único candidato, o Git o exibe:
a1b2c3d4e5f6... is the first bad commit
commit a1b2c3d4e5f6...
Author: João Silva <joao@example.com>
Date: Tue Sep 29 11:40:12 2026 -0300
Fazer cache do cálculo do carrinho
src/cart.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
5. Encerre a sessão
git bisect reset
O comando leva você de volta para a branch onde começou. Não pule esse passo: enquanto a sessão não for encerrada, você continua em detached HEAD, e commits novos se perdem com facilidade. A variante git bisect reset HEAD deixa você no commit encontrado, e git bisect reset <commit>, em qualquer commit indicado.
Busca automática: git bisect run
Se a verificação cabe em um comando, deixe todo o trabalho com o Git. O git bisect run executa um script a cada passo e marca o commit sozinho conforme o código de saída:
| Código de saída | Significado |
|---|---|
| 0 | good — o commit está bom |
| 1–127, exceto 125 | bad — o commit está ruim |
| 125 | skip — o commit não pode ser testado; pular |
| acima de 127 (ou <0) | interromper o bisect (por exemplo, o script morreu por um sinal) |
Exemplos 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 verificações mais complexas, escreva um script pequeno. É importante separar «não compilou» de «o bug se reproduziu», senão o bisect confunde um build quebrado com o erro que você procura:
#!/bin/sh
# check.sh
npm ci --silent || exit 125 # não conseguiu instalar as dependências — pular
npm run build || exit 125 # não compilou — pular
node scripts/check-cart-total.js # 0 — ok, 1 — bug
chmod +x check.sh
git bisect run ./check.sh
Mantenha o script fora dos arquivos rastreados ou numa pasta que exista em todos os commits testados: o bisect troca o diretório de trabalho, e um arquivo adicionado recentemente some nos commits antigos. O mais seguro é colocá-lo em /tmp ou em qualquer pasta fora do repositório.
Um teste que não existia antes
Situação comum: você escreveu um teste que reproduz o bug, mas nos commits antigos esse arquivo não existe. A solução é a mesma — uma cópia fora do repositório:
cp tests/test_regression.py /tmp/
git bisect run pytest /tmp/test_regression.py
git bisect skip: quando o commit não pode ser testado
Às vezes o commit do meio não compila ou falha por outro motivo. Marcá-lo como good ou bad distorceria o resultado. Pule-o:
git bisect skip
git bisect skip v2.4..v2.4.3 # pular um intervalo
O Git escolhe um commit vizinho. Se os commits pulados ficarem bem na fronteira, no fim você verá uma lista de «possíveis culpados» em vez de um só — aí é olhar os diffs deles manualmente.
Termos próprios no lugar de good e bad
O bisect não serve só para bugs. Por exemplo, você quer achar o commit a partir do qual a página passou a carregar mais rápido. Chamar commits rápidos de «ruins» confunde, por isso existem palavras alternativas:
git bisect start --term-old=slow --term-new=fast
git bisect fast HEAD
git bisect slow v2.3
Os sinônimos embutidos são old e new: git bisect new, git bisect old. A regra é uma só: o estado «antigo» tem que estar no commit mais antigo.
Registro da sessão: log, replay, visualize
git bisect log— mostra todas as suas marcações. Salve num arquivo se a sessão for longa.git bisect replay arquivo— repete a sessão a partir do registro. Errou no sexto passo? Salve o log, apague as linhas erradas, rodegit bisect resete depoisreplay.git bisect visualize(ougit bisect view) — mostra os candidatos restantes; com--oneline, exibe-os via git log.
git bisect log > /tmp/bisect.log
# editamos o log
git bisect reset
git bisect replay /tmp/bisect.log
Bisect e commits de merge
Num histórico com muitos merges, o bisect pode levar você para dentro de uma branch mesclada — para commits criados antes de a branch ser sincronizada com o main e, portanto, montados sobre outra base. Se o que importa primeiro é descobrir qual merge trouxe o bug, use:
git bisect start --first-parent
Assim o Git segue só pelos primeiros pais dos commits de merge, ou seja, pelo histórico da branch principal. Depois de achar o merge culpado, dá para rodar um segundo bisect dentro da branch correspondente.
Erros comuns
- Trocou good e bad. O resultado não fará sentido. Recupere a sessão com
git bisect logereplay, sem começar do zero. - Diretório de trabalho sujo. Alterações não commitadas atrapalham a troca de commits e afetam o teste. Antes de começar, faça um commit ou um
git stash. - Artefatos de build não limpos. Cache velho ou
node_modulesde outro commit geram resultados falsos. No script, faça um build limpo. - Teste instável (flaky). Se o teste às vezes falha sozinho, o bisect marca um commit aleatório. Rode a verificação várias vezes ou conserte o teste antes da busca.
- Esqueceu o reset. Ficou em detached HEAD e começou a trabalhar. O reflog ajuda a achar os commits perdidos.
Exemplo: caçando uma regressão do início ao fim
Usuários relataram que o total do carrinho sai errado quando há desconto. No release v2.3 estava certo; no main, não. Escrevemos uma verificação mínima e iniciamos a busca:
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 # guardar as alterações não commitadas
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
A busca inteira em uma centena de commits roda sem você, enquanto você cuida de outra coisa. O log salvo é útil se for preciso mostrar aos colegas, na tarefa, como exatamente o culpado foi encontrado.
O que fazer com o commit encontrado
Achado o culpado, veja-o por inteiro: git show a1b2c3d. Um commit pequeno, com mensagem clara, já entrega a causa — é um dos motivos por que as equipes valorizam commits atômicos: bisect num histórico de enormes «fix stuff» é quase inútil. Daí há dois caminhos: corrigir o erro com um novo commit ou, se a mudança inteira foi um equívoco e já está publicada, desfazê-la com git revert a1b2c3d.
Resumo
O git bisect transforma a caça a uma regressão, que seria ler centenas de diffs, em algumas respostas «funciona / não funciona» — e, com git bisect run, em um único comando. Você só precisa de duas referências — um commit bom e um ruim — e de uma verificação reproduzível. Leitura do histórico, navegação entre commits e busca de erros são trabalhadas na prática no curso de Git.
Perguntas frequentes
Quantos passos o git bisect leva?
Cerca de log2 do número de commits no intervalo: uns 7 passos para 100 commits e uns 10 para 1000. O próprio Git mostra a estimativa roughly N steps.
Dá para rodar o bisect sem saber qual commit é bom?
É preciso pelo menos um. Pegue um commit sabidamente antigo ou a tag de um release e teste-o manualmente; os passos extras por causa de um intervalo maior quase não custam nada.
O git bisect altera o histórico do repositório?
Não, ele só troca o diretório de trabalho entre commits e guarda as marcações em referências internas refs/bisect, que são apagadas no git bisect reset.
Posso indicar vários commits bons?
Sim, o git bisect good aceita vários commits, o que estreita o intervalo, especialmente em históricos com muitas branches.
O bisect serve só para achar bugs?
Não, ele encontra qualquer momento em que uma propriedade mudou: quando um arquivo apareceu, o build ficou mais rápido ou um comportamento mudou. Para isso, use os termos old/new ou os seus próprios com --term-old e --term-new.
Este tema é abordado em profundidade, com prática, no curso “Git do zero ao desenvolvedor confiante”.
Ir para o curso