Voltar ao Diminua Blog

Artigo

Desmistificando o Git Bisect: Encontrando Bugs em Históricos Complexos

Uma ferramenta poderosa para identificar a introdução de regressões em seus projetos, mesmo em históricos extensos.

Desmistificando o Git Bisect: Encontrando Bugs em Históricos Complexos

Introdução

Em projetos de software, especialmente aqueles em crescimento ou com históricos longos, identificar a origem exata de um bug pode ser uma tarefa árdua. Muitas vezes, um problema que se manifesta hoje pode ter sido introduzido semanas ou até meses atrás, em meio a centenas de commits. Métodos tradicionais como a revisão manual do histórico ou a tentativa e erro com testes podem consumir um tempo precioso. Felizmente, o Git oferece uma ferramenta poderosa para essa situação: o git bisect.

O git bisect utiliza uma abordagem de busca binária para encontrar o commit exato que introduziu um bug. Ao invés de verificar cada commit sequencialmente, ele testa o código em intervalos regulares, dividindo o histórico de commits ao meio a cada passo. Isso reduz drasticamente o número de verificações necessárias, tornando a identificação de bugs em históricos extensos uma tarefa muito mais eficiente.

O Problema: A Caça ao Bug em um Mar de Commits

Imagine que você descobriu um bug crítico em sua aplicação. Você sabe que em uma versão anterior, o código funcionava perfeitamente, mas agora ele está quebrado. O problema é que entre a versão funcional e a atual, houveram dezenas, centenas, ou até milhares de commits. Revisar cada um deles manualmente para encontrar a linha de código ou a alteração específica que causou o problema seria um processo extremamente demorado e frustrante. Você pode ter que:

  • Reverter commits um por um e testar.
  • Analisar visualmente cada alteração, o que é propenso a erros e demorado.
  • Depender da memória da equipe sobre quando o problema pode ter surgido.

Essas abordagens são ineficientes e não escalam bem com o crescimento do projeto e a complexidade do histórico.

A Solução: Git Bisect em Ação

O git bisect automatiza o processo de encontrar o commit problemático. Ele funciona com base em um princípio simples: você marca um commit conhecido como "bom" (onde o bug não existia) e um commit conhecido como "ruim" (onde o bug está presente). A partir daí, o Git seleciona um commit intermediário, você testa seu código nesse ponto e informa ao Git se ele é "bom" ou "ruim". O Git então ajusta o intervalo de busca e repete o processo até que o commit exato que introduziu o bug seja identificado.

Passo a Passo com git bisect

Vamos detalhar como usar o git bisect:

1. Iniciando a Sessão de Bisect

Para começar, você precisa entrar no modo bisect do Git:

git bisect start

Este comando prepara o Git para iniciar o processo de busca binária.

2. Marcando os Limites (Bom e Ruim)

Agora, você precisa informar ao Git qual commit é "bom" e qual é "ruim". O commit "ruim" geralmente é o seu HEAD atual (onde o bug é visível). O commit "bom" é uma versão anterior conhecida por estar funcionando corretamente.

Marque o commit "ruim":

git bisect bad

Marque um commit "bom" (substitua <commit_hash_bom> pelo hash do commit que você sabe que funciona):

git bisect good <commit_hash_bom>

Se você souber o nome de uma tag ou branch que representa um estado bom, pode usá-la também, por exemplo: git bisect good v1.0.

3. Testando e Marcando os Commits Intermediários

Após marcar os limites, o Git fará checkout de um commit no meio do intervalo. Agora, você precisa testar sua aplicação nesse commit para verificar se o bug está presente ou não.

Se o bug está presente neste commit intermediário, você marca-o como "ruim":

git bisect bad

Se o bug não está presente neste commit intermediário, você marca-o como "bom":

git bisect good

O Git então fará checkout de outro commit, novamente dividindo o intervalo restante ao meio, e você repetirá o processo de teste e marcação.

4. Conclusão da Busca

Continue este processo. A cada passo, o Git eliminará metade dos commits restantes. Eventualmente, o Git informará qual commit é o primeiro "ruim" e, portanto, o provável causador do bug.

O Git exibirá algo como:

<commit_hash_introduziu_bug> is the first bad commit
commit <commit_hash_introduziu_bug>
Author: ...
Date:   ...

    Mensagem do commit que introduziu o bug

 src/arquivo.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
5. Saindo do Modo Bisect

Uma vez que o commit problemático tenha sido identificado, você precisa sair do modo bisect para retornar ao seu branch original:

git bisect reset

Este comando desfaz todas as alterações feitas pelo git bisect start e o leva de volta ao estado em que você estava antes de iniciar o processo.

Automatizando os Testes com git bisect run

A etapa de testar o código a cada iteração pode ser manual e repetitiva. Se você tiver um script de teste automatizado que possa determinar se o bug está presente ou não (por exemplo, um teste unitário, de integração ou até mesmo um script que verifica a saída de um comando), você pode usar o git bisect run para automatizar todo o processo.

Seu script de teste deve:

  • Retornar 0 se o commit for "bom" (sem bug).
  • Retornar um código de saída entre 1 e 127 (exceto 125) se o commit for "ruim" (com bug).
  • Retornar 125 para pular o commit atual (por exemplo, se ele não compila ou não pode ser testado).

Para usar, execute:

git bisect run <seu_script_de_teste> [argumentos_para_o_script]

Por exemplo, se você tem um script chamado test.sh na raiz do seu projeto:

git bisect run ./test.sh

O Git executará o script para cada commit intermediário e marcará automaticamente como "good" ou "bad" com base no código de saída do script. Isso pode economizar uma quantidade enorme de tempo.

Dicas e Cuidados com git bisect

  • Scripts de Teste Confiáveis: A eficácia do git bisect run depende inteiramente da confiabilidade do seu script de teste. Certifique-se de que ele detecte o bug de forma precisa.
  • Commits Não Testáveis: Se um commit intermediário não compila ou não é testável por algum motivo (por exemplo, dependências ausentes que foram introduzidas posteriormente), você pode usar git bisect skip para pular esse commit. O Git tentará encontrar outro commit para testar.
  • Históricos Limpos: O git bisect funciona melhor em históricos onde os commits são relativamente pequenos e focados em uma única funcionalidade ou correção. Históricos com commits grandes e multifacetados podem dificultar a identificação do ponto exato da introdução do bug.
  • Entendendo o Contexto: Mesmo após o Git identificar o commit, é importante entender o contexto da alteração. Analise as linhas modificadas, a mensagem do commit e, se necessário, a tarefa ou ticket associado para compreender completamente a causa raiz.
  • Alternativas: Para fluxos de trabalho mais complexos ou quando o git bisect não é suficiente, ferramentas de análise de log e monitoramento podem ser úteis para correlacionar o surgimento do bug com eventos específicos. Ferramentas de observabilidade podem ajudar a rastrear o comportamento da aplicação em produção.

Conclusão

O git bisect é uma ferramenta indispensável no arsenal de qualquer desenvolvedor ou equipe que utiliza Git. Ele transforma a frustrante e demorada tarefa de caçar bugs em um processo eficiente e automatizado. Ao dominar o git bisect, você pode economizar horas de trabalho, identificar regressões mais rapidamente e manter a qualidade do seu código, mesmo em projetos com históricos complexos. Para fluxos de trabalho mais avançados em projetos maiores, a integração com sistemas de CI/CD e ferramentas de observabilidade pode complementar o poder do Git Bisect, garantindo que seus sistemas permaneçam estáveis e confiáveis.

Foto de Daniil Komov no Pexels.