Um assistente de IA sugere remover uma verificação de uma pequena função que lê linhas. “O chamador já fornece números de linha válidos”, diz ele. A função parece simples. Antes de aceitar a mudança, você consulta o histórico Git, e a linha aponta para um commit chamado “Compact comparison spacing”.

Isso oferece outro lugar para investigar. As perguntas úteis são qual entrada a verificação protege, quando esse comportamento surgiu e se os chamadores atuais ainda precisam dele. Um pequeno histórico que você pode reconstruir torna essas perguntas concretas.

Comece pela linha suspeita

Suponha que um visualizador de código-fonte use números de linha começando em um. Sua função atual é assim:

def read_line(lines, line_number):
    """Read a source line using a one-based line number."""
    if line_number<1:
        raise ValueError("line number must be positive")
    if line_number > len(lines):
        raise IndexError("line number is past the end")
    return lines[line_number - 1]

Para as duas linhas ["alpha", "omega"], a linha 1 deve retornar alpha e a linha 2, omega. Em Python, porém, o índice -1 lê o último elemento. Sem a verificação do limite inferior, o número de linha 0 vira o índice -1 e retorna omega. Um número de linha -1 vira o índice -2 e retorna alpha.

Você pode explicar isso pelo código atual. O histórico responde outra pergunta: quando essa rejeição passou a fazer parte da função e o que a mudança incluiu?

Reconstrua os quatro commits

Baixe git-history-lab.py para uma pasta de trabalho vazia. Você precisa de Git 2.29+ e Python 3.9+; não há dependências de pacotes. Execute no seu terminal:

python3 git-history-lab.py create history-lab --record history-check
cd history-lab
git log --reverse --format='%h %s' HEAD

No Windows, use seu inicializador de Python 3 no lugar de python3, se necessário. O fluxo usa Git e Python, sem APIs de macOS. A execução registrada usou Git 2.50.1 e Python 3.9.6 no macOS; Windows e Linux não foram testados separadamente.

O script cria um novo repositório local, executa as verificações e salva as saídas em history-check. Ele recusa um destino existente, usa um autor fictício só nos próprios commits e não altera sua configuração global do Git. Todos os nomes, datas, mensagens de commit e o cenário do visualizador são fixtures de ensino.

ce4c0cf Add one-based source line lookup
e87088e Reject nonpositive source line numbers
7d41ebc Rename line reader module
27a0dc5 Compact comparison spacing

Cada commit também tem uma tag legível:

Tag Mudança
baseline Adiciona line_reader.py; os testes cobrem a primeira linha, a última e uma entrada além do fim
fix-positive-lines Adiciona a verificação do limite inferior e testes para 0, -1 e uma lista de entrada vazia
rename-module Renomeia o arquivo para source_lines.py e atualiza o import do teste
format-spacing Muda line_number < 1 para line_number<1

O espaçamento compacto chama atenção de propósito para facilitar o reconhecimento da última edição.

Use blame para encontrar o próximo diff

Inspecione a linha 3:

git blame -L 3,3 HEAD -- source_lines.py
git show --format=fuller format-spacing -- source_lines.py

O blame atribui a linha a 27a0dc57, o commit de espaçamento. Seu patch contém:

-    if line_number < 1:
+    if line_number<1:

Agora peça ao Git para ignorar espaços em branco ao rastrear a linha:

git blame -w -L 3,4 HEAD -- source_lines.py

O resultado aponta para e87088e0 e o caminho antigo, line_reader.py. O git blame segue automaticamente renomeações de arquivos inteiros; -w ignora espaços em branco na atribuição. A renomeação em si não causou o resultado enganoso aqui; foi a edição posterior do espaçamento. Consulte a documentação oficial do git-blame.

Para esta edição específica, o verificador também compara árvores sintáticas de Python e executa os testes antes e depois. Os dois concordam. Ignorar espaços em branco é uma forma de investigar a atribuição, não um teste de comportamento: indentação e espaços dentro de strings podem importar em Python.

Depois de inspecionar um commit de formatação conhecido, você também pode excluí-lo explicitamente:

git blame --ignore-rev format-spacing -L 3,3 HEAD -- source_lines.py

Isso também chega à correção neste laboratório. Não exclua um commit só porque o título diz “format”; leia primeiro seu patch. O blame registra a atribuição de linhas, não o raciocínio de um desenvolvedor nem sua responsabilidade por um bug.

Busque o texto que sobrevive à reescrita

A mensagem de erro permaneceu igual durante a renomeação e a edição de espaçamento. Busque sua introdução:

git log --format='%h %s' -p -S 'line number must be positive' HEAD -- '*.py'

Isso retorna a correção e seu patch em line_reader.py. Colocar *.py entre aspas deixa a correspondência de caminhos a cargo do Git e inclui o nome antigo. Este repositório só tem um commit que adiciona ou remove essa mensagem.

-S seleciona mudanças no número de ocorrências de uma string. Ele não seleciona toda edição perto de uma ocorrência. Consulte a explicação de pickaxe do Git.

Experimente uma string que mudou durante a formatação:

git log --format='%h %s' -p -S 'line_number < 1' HEAD -- '*.py'

Agora a saída inclui tanto o commit de espaçamento, que remove essa grafia exata, quanto a correção, que a adiciona. A ocorrência extra é útil: leia o que mudou e escolha um fragmento mais estável, se necessário. A ausência de resultado também precisa de interpretação; o texto pode mudar sem alterar sua quantidade de ocorrências.

Para um arquivo conhecido, este laboratório também funciona com:

git log --follow --format='%h %s' -p -S 'line number must be positive' HEAD -- source_lines.py

--follow acompanha o histórico de renomeações de um arquivo. Não é uma busca geral entre arquivos. Em uma investigação real, confira caminhos antigos ou amplie a seleção de caminhos quando uma busca restrita parar cedo demais.

Siga as linhas no tempo com log -L

Se você já conhece o intervalo relevante, peça seus patches ao longo do histórico:

git log --format='%h %s' -L 3,4:source_lines.py HEAD

Neste repositório, a saída inclui a edição de espaços em branco e a adição original da verificação. Ela atravessa a renomeação pura do arquivo, mas o bloco renomeado sem mudanças não recebe um patch separado. Use o log anterior de quatro commits quando precisar da sequência inteira.

Para a função inteira, execute:

git log --format='%h %s' -L :read_line:source_lines.py HEAD

Isso também chega à expressão de indexação original em baseline. O laboratório declara *.py diff=python em .gitattributes para o Git usar suas regras de cabeçalho de função Python. O reconhecimento de funções depende da linguagem e da configuração do diff; consulte definição de um cabeçalho de trecho personalizado. Uma alternativa numérica para este arquivo é:

git log --format='%h %s' -L 1,7:source_lines.py HEAD

Com log -L, o caminho fica dentro do argumento de intervalo; não acrescente um limitador -- path. O intervalo precisa existir na revisão inicial, e o percurso começa em uma única revisão. Essas restrições estão na documentação oficial do git-log.

Históricos reais podem conter merges, código copiado, funções reescritas ou commits antigos ausentes em um clone superficial. Rastrear uma linha é um ponto de partida para ler patches; isso não recupera uma justificativa que nunca foi registrada.

Teste a explicação nas duas versões

Leia a implementação e os testes juntos na correção encontrada:

git show --format=fuller fix-positive-lines -- line_reader.py test_lines.py

O patch adiciona a verificação do limite inferior e um teste que espera ValueError para 0 e -1. O verificador do laboratório extrai o código e os testes dos commits para pastas temporárias. Ele executa a mesma suíte de regressão contra a implementação inicial e depois os testes próprios de cada commit. Ele não troca seu checkout.

Observação da execução registrada O que ela sustenta
A versão inicial retorna omega para 0 e alpha para -1 Essas entradas chegam à indexação negativa do Python
A suíte da correção falha na versão inicial para ambas as entradas As asserções adicionadas detectam o comportamento antigo
As versões de correção, renomeação e espaçamento passam cinco testes cada Esses casos testados se comportam como esperado após a correção
A renomeação preserva os bytes do código; o espaçamento preserva a árvore sintática Essas edições posteriores preservam a implementação desta função

Os três testes originais da versão inicial também passam. Uma suíte aprovada pode simplesmente deixar de fora a entrada que interessa a você. Os resultados esperados completos descrevem as verificações; seu history-check/transcript.txt contém os comandos e saídas reais, incluindo as duas falhas esperadas.

A mensagem do commit de exemplo descreve a linha zero retornando a última linha; os testes reproduzem esse comportamento. Antes de remover a verificação no seu projeto, veja como os chamadores atuais rejeitam zero e números negativos. O exemplo pressupõe entradas inteiras; outros tipos precisam de validação separada.

Dê à IA uma pergunta verificável

Quando tiver os IDs das revisões, o patch e a saída dos testes, peça ao assistente para relacioná-los:

Explique por que esta verificação de limite inferior existe. Use a correção fornecida e seu commit pai, mostre o resultado das entradas 0 e -1 e identifique as asserções de regressão. Separe o que o diff e os testes executados mostram do que a mensagem do commit afirma. Depois inspecione os chamadores atuais antes de recomendar a remoção. Se esses chamadores ou relatos históricos não estiverem disponíveis, diga qual conclusão continua em aberto.

Uma resposta útil tem três partes: a mudança de comportamento, a motivação relatada e o que continua desconhecido sobre os chamadores atuais. Se ela afirmar que um incidente ocorreu só porque a mensagem diz isso, peça uma afirmação mais restrita. Se propuser apagar a verificação, peça para mostrar como cada caminho de entrada suportado elimina as entradas que falham. Seguir um valor por código desconhecido é um bom próximo passo para investigar os chamadores; revisar um patch escrito por IA mostra como transformar uma explicação proposta em um teste de regressão específico.