Pular para o conteúdo
Programação

Git bisect: como encontrar o commit que introduziu um bug

O git bisect encontra, dentro do histórico do Git, o primeiro commit em que um bug apareceu. Em vez de revisar dezenas de mudanças uma por uma, você informa uma versão conhecida como boa, outra com problema e testa apenas pontos escolhidos pelo próprio Git.

O recurso é útil quando o programa funcionava em algum momento do passado, deixou de funcionar depois de várias alterações e você não sabe qual mudança causou a regressão. Ele não corrige o bug sozinho: reduz o campo de investigação até apontar o commit que merece ser analisado.

Diagrama dos objetos do Git, mostrando commits, árvores e blobs relacionados
O Git guarda commits e os objetos que formam cada versão do projeto. Diagrama de Bunyk, CC BY-SA 4.0, via Wikimedia Commons.

O que é o git bisect?

git bisect é um comando que usa uma busca binária no histórico do projeto. Pense em um dicionário: para encontrar uma palavra, você não começa pela primeira página; abre no meio e decide se deve procurar antes ou depois. O bisect faz algo parecido com os commits.

Você fornece dois limites:

  • good: um commit em que o projeto ainda se comportava corretamente;
  • bad: um commit em que o bug já podia ser reproduzido.

O Git escolhe uma versão entre esses dois pontos, faz o checkout dela e espera que você execute um teste. Se o problema existir, marque aquela versão como bad; se não existir, marque como good. A cada resposta, metade das possibilidades deixa de ser suspeita.

A documentação oficial do Git sobre bisect também descreve usos mais amplos. O comando pode procurar a mudança que introduziu uma melhoria de desempenho, uma correção ou qualquer outra propriedade verificável. Nesse caso, os termos old e new podem ser mais claros que “bom” e “ruim”. O capítulo Debugging with Git, do livro Pro Git, traz um exemplo de investigação manual e automação com um script.

Quando vale a pena usar esse comando

O bisect faz sentido quando você consegue responder “funciona” ou “não funciona” para cada versão testada. Alguns exemplos:

  • uma tela passou a quebrar depois de uma sequência de refatorações;
  • uma consulta ficou lenta, mas a equipe não sabe em qual alteração isso começou;
  • uma API deixou de aceitar determinada entrada depois de vários commits;
  • um teste automatizado passou a falhar em algum ponto da história;
  • um projeto de equipe precisa identificar a alteração responsável antes de escolher a correção.

Ele é menos útil quando o problema não é reproduzível, depende de um serviço externo instável ou muda de comportamento sem relação consistente com o commit testado. A busca binária precisa de uma resposta confiável para cada etapa.

Antes de começar: preserve seu trabalho

Durante uma sessão de bisect, o Git troca o commit em checkout várias vezes. Antes de iniciar, faça commit ou stash das alterações locais que não fazem parte da investigação. Também confirme que o projeto consegue ser instalado, compilado ou executado nas versões antigas que serão analisadas.

Se dependências, banco de dados ou variáveis de ambiente mudaram ao longo do tempo, anote o que cada commit precisa para funcionar. Um commit que falha porque o ambiente atual ficou incompatível não deve ser automaticamente classificado como o primeiro commit com bug.

Como usar o git bisect manualmente

1. Inicie a sessão

Abra o terminal na cópia local do repositório e inicie uma bisseção:

git bisect start

Agora marque a versão que está com problema. Se o commit atualmente aberto é o que reproduz o bug, basta executar:

git bisect bad

Depois, informe um commit antigo que você sabe que funcionava. Pode ser uma tag, como v1.0, ou um hash:

git bisect good v1.0

Também é possível passar os limites diretamente ao iniciar:

git bisect start HEAD v1.0

O Git escolherá um commit intermediário e fará o checkout dele. A mensagem no terminal informa qual revisão foi selecionada e quantas ainda podem existir no intervalo.

2. Rode sempre o mesmo teste

Agora reproduza o problema na versão que o Git selecionou. O teste pode ser abrir uma página, executar um comando, rodar uma suíte ou conferir uma saída específica. O importante é manter o critério igual em todas as revisões.

Se o bug ainda estiver presente, marque o commit:

git bisect bad

Se o comportamento estiver normal, marque-o como bom:

git bisect good

Depois de cada resposta, o Git seleciona outra revisão. Repita o processo até ele exibir a mensagem is the first bad commit. Esse é o primeiro commit do intervalo em que o teste passou a falhar; ele não significa necessariamente que o autor pretendia introduzir o problema.

Fluxo de operações do Git entre arquivos de trabalho, área de stage, repositório local e remoto
O bisect investiga versões registradas no repositório local; ele não substitui o fluxo normal de commit e revisão. Diagrama de Daniel Kinzler, CC BY 3.0, via Wikimedia Commons.

3. Leia a alteração encontrada

Quando o commit for identificado, examine o que mudou:

git show --stat <hash-do-commit>
git show <hash-do-commit>

O primeiro comando resume os arquivos alterados. O segundo mostra o diff, isto é, as linhas que entraram e saíram. A partir daí, a investigação deixa de procurar em toda a história e passa a avaliar uma mudança concreta, seus efeitos colaterais e o teste que faltou.

4. Encerre a sessão

Ao terminar, volte para o branch e para o commit que estavam ativos antes do bisect:

git bisect reset

Não pule esse passo. Durante a investigação, o Git pode deixar o repositório em um estado de “cabeça desacoplada” (detached HEAD), no qual você está olhando uma revisão específica em vez de trabalhar normalmente no branch.

O que fazer quando um commit não pode ser testado

Às vezes a revisão escolhida não compila, depende de uma ferramenta que não existe mais ou falha por um motivo diferente do bug procurado. Nessa situação, não marque o commit automaticamente como bad. Isso ensinaria ao Git uma informação errada.

Use:

git bisect skip

O Git tentará escolher outra revisão. Há uma limitação importante: se você pular justamente a região em que está o primeiro commit ruim, o comando pode não conseguir apontar um único commit com certeza. Quando isso acontecer, compare os candidatos retornados e amplie o teste manualmente.

Como automatizar com git bisect run

Se você já tem um teste que retorna sucesso ou falha, pode deixar o Git repetir a bisseção sozinho. Primeiro delimite o intervalo e depois passe o script:

git bisect start HEAD v1.0
git bisect run ./teste-regressao.sh

O script precisa devolver códigos de saída que o Git consiga interpretar:

  • 0 indica que a versão passou no teste, portanto é boa;
  • um código de 1 a 124 indica falha, portanto a versão é ruim;
  • 125 informa que aquela revisão não pôde ser testada e deve ser pulada;
  • outros códigos interrompem o processo, em vez de classificar a revisão.

Um script simples pode ser assim:

#!/bin/sh
npm test -- --runInBand

Se o comando de teste retornar zero quando tudo estiver certo e um código diferente de zero quando o caso falhar, o próprio shell fornece a informação necessária. Para projetos em Python, Java, PHP ou outra linguagem, a lógica é a mesma: o bisect não depende da linguagem, apenas do resultado verificável.

Antes de automatizar, confirme que o teste é determinístico. Um teste que falha às vezes por causa de rede, horário ou concorrência pode levar o Git a uma conclusão errada. Se a preparação de uma versão falhar por motivo externo, o script deve tratar esse caso conscientemente, muitas vezes retornando 125.

Logotipo do Git em laranja e branco sobre fundo escuro, usado para ilustrar o guia de git bisect
O git bisect faz parte do próprio Git e funciona independentemente da linguagem usada no projeto. Logotipo por Jason Long, CC BY 3.0, via Wikimedia Commons.

Erros comuns no git bisect

  • Escolher um “good” que nunca foi testado: um limite errado contamina toda a busca. Use uma versão realmente conhecida.
  • Usar testes diferentes em cada commit: se o critério muda, as respostas deixam de ser comparáveis.
  • Confundir falha de compilação com o bug: uma revisão antiga pode precisar de dependências ou comandos diferentes.
  • Esquecer alterações locais: o checkout de outras revisões pode misturar arquivos de trabalho com o estado investigado.
  • Não executar git bisect reset: você pode continuar em uma revisão intermediária sem perceber.
  • Tratar o primeiro commit ruim como culpa individual: o commit é um ponto técnico de investigação; a causa pode ser uma interação com outra mudança.

Git bisect ou git blame: qual usar?

Os dois comandos ajudam a investigar histórico, mas respondem perguntas diferentes. O git blame mostra qual commit alterou cada linha de um arquivo. Ele é uma boa escolha quando você já sabe qual linha ou trecho parece suspeito.

O git bisect é melhor quando o problema é observável, mas a origem ainda está espalhada por muitas mudanças. Se você não sabe sequer qual arquivo foi afetado, comece com bisect; se o commit encontrado mexeu em uma linha específica, use blame ou git show para aprofundar.

Uma ferramenta de IA pode ajudar a interpretar o diff, sugerir hipóteses ou escrever um teste, mas não deve substituir a reprodução do problema. O guia do JSMS sobre GitHub Copilot e seus recursos ajuda a entender essa camada de assistência; o bisect continua sendo o mecanismo que verifica em qual versão o comportamento mudou.

Conclusão: o git bisect economiza revisão inútil

Use o git bisect quando você tiver um commit bom, um commit ruim e um teste repetível. O comando não é mágico e não descobre a intenção por trás de uma alteração, mas troca uma caça cansativa em dezenas de commits por uma sequência curta de respostas objetivas.

O fluxo mais seguro é: preserve suas alterações, delimite o intervalo, teste cada revisão com o mesmo critério, use skip quando a versão não for testável, examine o diff encontrado e finalize com git bisect reset. Em projetos com testes automatizados, git bisect run transforma a técnica em uma ferramenta prática de diagnóstico.

Perguntas frequentes

Preciso saber o hash exato do commit bom?

Não. Você pode usar uma tag, uma referência como HEAD~20 ou o hash completo ou abreviado. O importante é que essa revisão seja conhecida como funcional para o teste escolhido.

O git bisect altera ou apaga meus commits?

Não. Ele troca a revisão em checkout para permitir os testes. Ainda assim, preserve alterações não commitadas antes de começar e use git bisect reset no final para retornar ao estado anterior.

Posso usar o git bisect para investigar desempenho?

Sim. Se o teste de desempenho tiver um critério consistente, você pode procurar a mudança que transformou uma condição antiga em uma nova. A documentação permite usar os termos old e new para esse tipo de investigação.

O que significa o código 125 no git bisect run?

O código 125 informa que a revisão atual não pôde ser testada. O Git pula esse ponto, mas muitos commits não testáveis podem reduzir a certeza sobre qual foi o primeiro commit com problema.