Git notes: adicionando metadados a commits sem reescrever

1. O que são Git notes e por que usá-las?

Git notes são objetos especiais do Git que permitem anexar informações adicionais a commits sem alterar o hash SHA do commit original. Diferentemente de git commit --amend, que reescreve o histórico e modifica o identificador do commit, as notas são armazenadas em uma referência separada (refs/notes/) e associadas ao commit por meio de um ponteiro interno.

Essa característica é crucial porque:
- O SHA do commit permanece inalterado, preservando assinaturas GPG e referências externas
- Notas podem ser adicionadas, editadas ou removidas sem impactar outros desenvolvedores
- Permite enriquecer commits antigos com informações que não estavam disponíveis no momento da criação

Casos de uso típicos incluem:
- Anotações de revisão de código após o merge
- Metadados de CI/CD (status de build, resultados de testes)
- Documentação pós-commit (explicações tardias, links para issues)
- Informações de auditoria e conformidade

2. Criando e visualizando notas

O comando básico para adicionar uma nota a um commit é:

git notes add -m "Aprovado na revisão de código em 2024-01-15" abc1234

Para visualizar a nota de um commit específico:

git notes show abc1234

Para ver todas as notas associadas aos commits no log:

git log --notes

Você também pode adicionar notas a múltiplos commits de uma vez usando um loop no shell:

for commit in $(git rev-list main~5..main); do
  git notes add -m "Commit revisado pelo time de QA" $commit
done

3. Editando, removendo e gerenciando notas

Para editar uma nota existente:

git notes edit abc1234

Isso abrirá o editor configurado. Se preferir substituir a nota diretamente pela linha de comando:

git notes add -f -m "Nova versão da nota" abc1234

Para remover uma nota:

git notes remove abc1234

Para listar todas as notas do repositório, mostrando o commit associado e o conteúdo:

git notes list

A saída típica mostra pares de hash (nota -> commit):

d1e2f3g4h5i6j7k8l9m0n1o2p3q4r5s6t7u8v9w0 abc1234def5678901234567890123456789012345

4. Notas como metadados estruturados

Notas não precisam ser apenas texto simples. Você pode armazenar JSON, YAML ou qualquer formato estruturado. Exemplo prático: adicionar resultado de testes automáticos a commits.

Adicionando uma nota com JSON:

git notes add -m '{"status": "passed", "coverage": 87.5, "duration_sec": 42}' abc1234

Para extrair e processar notas com scripts, use o comando git notes show combinado com ferramentas como jq:

git notes show abc1234 | jq '.coverage'

Exemplo em Python para processar notas de múltiplos commits:

import subprocess
import json

commits = subprocess.check_output(['git', 'rev-list', 'HEAD~10..HEAD']).decode().split()
for commit in commits:
    try:
        note = subprocess.check_output(['git', 'notes', 'show', commit]).decode()
        data = json.loads(note)
        print(f"Commit {commit[:8]}: status={data['status']}, coverage={data['coverage']}%")
    except subprocess.CalledProcessError:
        print(f"Commit {commit[:8]}: sem nota")

5. Compartilhando notas com a equipe

Por padrão, git push não envia notas. Para compartilhá-las com o repositório remoto:

git push origin refs/notes/*

Para receber notas de outros desenvolvedores:

git fetch origin refs/notes/*:refs/notes/*

Para configurar o comportamento automático, edite o arquivo .git/config ou use:

git config --add remote.origin.fetch "+refs/notes/*:refs/notes/*"

Agora, git fetch e git pull trarão as notas automaticamente.

6. Notas avançadas: namespaces e separação lógica

Você pode organizar notas em namespaces diferentes, cada um armazenado em uma referência separada. Isso é útil para separar contextos como revisão de código, CI e deploy.

Criando um namespace para revisões:

git notes --ref=reviews add -m "Aprovado após 3 rodadas de revisão" abc1234

Criando um namespace para CI:

git notes --ref=ci add -m "Build #4521 - Sucesso" abc1234

Para visualizar notas de um namespace específico:

git log --notes=reviews
git log --notes=ci

Para enviar apenas um namespace específico para o remoto:

git push origin refs/notes/reviews

Exemplo prático: separar notas de revisão de código de notas de deploy:

# Nota de revisão
git notes --ref=reviews add -m "Código revisado por Maria - sem problemas críticos" def5678

# Nota de deploy
git notes --ref=deploy add -m "Deploy realizado em produção em 2024-01-20 às 14:30 UTC" def5678

7. Limitações e boas práticas

Limitações importantes:

  • Notas não são versionadas como commits. Se dois desenvolvedores adicionarem notas ao mesmo commit simultaneamente, podem ocorrer conflitos que exigem resolução manual.
  • Em repositórios com milhares de notas, o desempenho do git log --notes pode degradar. Considere usar namespaces e filtrar apenas os necessários.
  • Notas não são incluídas em git clone por padrão — é preciso configurar explicitamente o fetch.

Boas práticas:

  • Use namespaces para separar contextos claramente (reviews, ci, deploy, auditoria)
  • Documente o formato das notas (JSON schema, campos obrigatórios) para a equipe
  • Configure o fetch automático de notas no repositório compartilhado
  • Evite notas para informações críticas que precisam ser imutáveis — prefira commits reais ou tags assinadas
  • Limpe notas obsoletas periodicamente para evitar acúmulo desnecessário

Quando NÃO usar notas:

  • Para informações que devem fazer parte do histórico imutável do commit (prefira git commit --amend ou um novo commit)
  • Para dados que precisam ser assinados criptograficamente (use tags GPG)
  • Em workflows onde todos os desenvolvedores precisam ver as notas imediatamente sem configuração adicional

Referências