Como usar git bisect para encontrar o commit que introduziu o bug

1. Introdução ao Git Bisect: A Busca Binária no Histórico

Encontrar um bug em um projeto com centenas ou milhares de commits pode ser uma tarefa exaustiva. Imagine um repositório com 500 commits: verificar manualmente cada um deles para identificar onde um problema foi introduzido consumiria horas ou dias de trabalho. É aqui que o git bisect se torna uma ferramenta indispensável.

O git bisect aplica o princípio da busca binária ao histórico do Git. Em vez de verificar cada commit individualmente, ele divide o intervalo de commits pela metade a cada iteração, reduzindo drasticamente o número de verificações necessárias. Com 500 commits, você precisaria de no máximo 9 verificações (log₂ 500 ≈ 9) para encontrar o commit problemático.

Os pré-requisitos básicos para usar o bisect são:
- Um commit conhecido como "bom" (onde o bug não existia)
- Um commit conhecido como "ruim" (onde o bug está presente)
- Um método para testar se o bug existe em um commit específico

2. Iniciando uma Sessão de Bisect

Para iniciar uma sessão de bisect, primeiro você precisa estar no repositório Git do projeto e identificar os commits "bom" e "ruim". O fluxo básico é:

# Iniciar a sessão de bisect
git bisect start

# Marcar o commit atual como "ruim" (onde o bug está presente)
git bisect bad

# Marcar um commit antigo como "bom" (onde o bug não existia)
git bisect good <hash-do-commit-bom>

Vamos a um exemplo prático. Suponha que você está trabalhando em um sistema de login e um bug foi introduzido recentemente:

# Verificar o histórico recente
git log --oneline --graph -10

# Saída exemplo:
# * a1b2c3d - Correção de segurança no login
# * e4f5g6h - Implementação de autenticação OAuth
# * i7j8k9l - Refatoração do módulo de usuários
# * m0n1o2p - Adição de validação de senha
# * q3r4s5t - Correção de bug no logout
# * u6v7w8x - Versão estável 2.0

# Iniciar bisect
git bisect start
git bisect bad  # marca HEAD como ruim (bug presente)
git bisect good u6v7w8x  # marca commit estável como bom

O Git então calcula o ponto médio do intervalo e faz checkout automático para o commit mediano:

# Saída do Git:
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[i7j8k9l] Refatoração do módulo de usuários

3. O Ciclo de Testes Automatizado

A forma mais eficiente de usar o git bisect é com testes automatizados. O comando git bisect run permite executar um script automaticamente em cada commit intermediário.

Crie um script de teste que retorne:
- Código 0: commit é "bom" (bug não presente)
- Código 1-127: commit é "ruim" (bug presente)
- Código 125: commit deve ser ignorado (não compila, por exemplo)

Exemplo de script de teste (test_login.sh):

#!/bin/bash

# Script para testar o sistema de login
# Retorna 0 se o login funciona, 1 se falha

./build.sh 2>/dev/null || exit 125  # Se não compilar, ignora

# Executa teste de login
./test_login.py --test-basic-auth
if [ $? -eq 0 ]; then
    exit 0  # Login funciona - commit bom
else
    exit 1  # Login quebrado - commit ruim
fi

Agora execute o bisect automatizado:

# Tornar o script executável
chmod +x test_login.sh

# Executar bisect automatizado
git bisect run ./test_login.sh

O Git executará o script em cada commit mediano, alternando entre commits "bom" e "ruim" até encontrar o culpado:

# Saída exemplo do bisect run:
running ./test_login.sh
Bisecting: 2 revisions left to test after this (roughly 1 step)
[e4f5g6h] Implementação de autenticação OAuth

running ./test_login.sh
Bisecting: 1 revision left to test after this (roughly 0 steps)
[a1b2c3d] Correção de segurança no login

running ./test_login.sh
e4f5g6h is the first bad commit
commit e4f5g6h
Author: Maria Silva
Date:   Mon Jan 15 14:30:00 2024 -0300

    Implementação de autenticação OAuth

4. Estratégias para Testes Manuais Eficientes

Quando não é possível automatizar os testes, você pode realizar testes manuais. O Git informa quantos passos restam, ajudando a planejar o trabalho:

# Após iniciar o bisect
Bisecting: 7 revisions left to test after this (roughly 3 steps)

# Para cada commit, teste manualmente e marque:
git bisect good  # se o bug não está presente
git bisect bad   # se o bug está presente

Dicas para testes manuais eficientes:
- Mantenha um checklist dos passos de teste
- Use git bisect visualize para ver o progresso graficamente com gitk
- Documente os resultados parciais com git bisect log

Para visualizar o progresso:

# Abre interface gráfica mostrando os commits testados
git bisect visualize

# Ou use gitk diretamente
gitk --bisect

5. Lidando com Commits Problemáticos

Nem todos os commits são testáveis. Commits que não compilam, quebram o ambiente ou são irrelevantes para o teste podem ser pulados:

# Pular um commit problemático manualmente
git bisect skip

# Ou usar o código 125 em scripts automatizados
exit 125  # No script de teste

Exemplo de script mais robusto que lida com problemas de compilação:

#!/bin/bash

# Tenta compilar o projeto
if ! make clean && make; then
    echo "Falha na compilação - ignorando commit"
    exit 125
fi

# Executa suite de testes
if python3 -m pytest tests/test_login.py; then
    exit 0  # Testes passam
else
    exit 1  # Testes falham
fi

O Git automaticamente selecionará outro commit para testar quando encontrar um código 125.

6. Finalizando o Bisect e Corrigindo o Bug

Quando o bisect encontra o commit culpado, você pode gerar um relatório completo e resetar o estado:

# Gerar log completo da sessão de bisect
git bisect log > bisect_report.txt

# O conteúdo do relatório inclui:
# git bisect start
# git bisect bad
# git bisect good u6v7w8x
# git bisect good i7j8k9l
# git bisect bad e4f5g6h
# # first bad commit: [e4f5g6h] Implementação de autenticação OAuth

# Resetar o estado do bisect
git bisect reset

Agora você pode analisar o commit identificado:

# Ver detalhes do commit problemático
git show e4f5g6h

# Criar um branch para trabalhar na correção
git checkout -b fix-oauth-bug e4f5g6h~1

7. Técnicas Avançadas e Boas Práticas

Bisect em branches com merge commits:

Para projetos com merges complexos, use a opção --first-parent para seguir apenas o primeiro pai de cada merge:

git bisect start --first-parent
git bisect bad
git bisect good v2.0
git bisect run ./test_script.sh

Repetindo sessões de bisect com replay:

Se você precisa repetir uma sessão anterior, use o arquivo de log:

# A partir do relatório gerado anteriormente
git bisect replay bisect_report.txt

Integração com CI/CD:

Configure pipelines de CI para executar bisect automaticamente quando um bug é reportado:

# Exemplo de script para CI/CD
#!/bin/bash

COMMIT_BOM=$1
COMMIT_RUIM=$2

git bisect start
git bisect bad $COMMIT_RUIM
git bisect good $COMMIT_BOM
git bisect run ./ci_test_suite.sh

# Enviar resultado para sistema de tracking
git bisect log | curl -X POST -d @- https://api.bugtracker.com/report

Boas práticas:
- Sempre mantenha commits pequenos e focados para facilitar o bisect
- Documente commits que quebram a compilação para uso futuro com git bisect skip
- Use tags para marcar versões estáveis como pontos de referência "bom"
- Integre testes automatizados que possam ser executados rapidamente

O git bisect é uma ferramenta poderosa que transforma a caça a bugs em um processo sistemático e eficiente, economizando horas de trabalho manual e permitindo que equipes identifiquem rapidamente a origem de problemas em seus projetos.

Referências