Como manter um histórico de commits limpo e legível
1. Por que um histórico de commits limpo é essencial?
Um histórico de commits bem organizado é a espinha dorsal de qualquer projeto de software de longo prazo. Imagine tentar entender as mudanças em um sistema complexo apenas por mensagens como "correções" ou "atualizações" — isso rapidamente se torna um pesadelo.
Facilidade de revisão de código e rastreamento de bugs
Quando cada commit representa uma mudança lógica e clara, revisar código se torna uma tarefa objetiva. Você pode identificar exatamente qual alteração introduziu um bug, economizando horas de depuração.
Colaboração eficiente em equipe e onboarding
Novos desenvolvedores conseguem entender a evolução do projeto lendo o histórico. Commits descritivos funcionam como documentação viva do raciocínio por trás das decisões técnicas.
Automação de changelogs e releases
Ferramentas como standard-version ou semantic-release geram changelogs automaticamente a partir de commits padronizados, eliminando trabalho manual e reduzindo erros.
2. Princípios fundamentais de uma boa mensagem de commit
A estrutura clássica de uma mensagem de commit segue três partes:
Título (máximo 50 caracteres)
[linha em branco]
Corpo (máximo 72 caracteres por linha)
[linha em branco]
Rodapé (opcional, para referências ou breaking changes)
Uso de verbos no imperativo
Sempre use o imperativo: "Adiciona", "Corrige", "Remove". Isso torna o commit um comando que, se aplicado, produz o resultado descrito.
Evitar mensagens vagas
Mensagens como "correções" ou "atualizações" não comunicam nada. Prefira:
Corrige cálculo de frete para pedidos acima de R$ 200
Em vez de:
correcoes
3. Convenções de nomenclatura: Conventional Commits
O padrão Conventional Commits (conventionalcommits.org) trouxe uma estrutura formal que facilita a automação. O formato básico é:
tipo(escopo opcional): descrição
Tipos comuns:
| Tipo | Uso |
|---|---|
feat |
Nova funcionalidade |
fix |
Correção de bug |
docs |
Alterações em documentação |
refactor |
Refatoração sem mudança de comportamento |
test |
Adição ou modificação de testes |
chore |
Tarefas de manutenção |
Exemplo prático:
feat(auth): adiciona login OAuth com Google
Implementa autenticação via OAuth 2.0 usando o provedor Google.
Inclui fluxo completo de autorização e callback.
Closes #42
Integração com versionamento semântico (SemVer)
- fix → incrementa versão PATCH
- feat → incrementa versão MINOR
- feat! ou BREAKING CHANGE → incrementa versão MAJOR
4. Técnicas para escrever commits atômicos
Um commit atômico representa uma única mudança lógica. Isso significa que cada commit deve ser autossuficiente e fazer sentido isoladamente.
Uso de git add -p
O comando git add -p (ou git add --patch) permite selecionar partes específicas de um arquivo modificado para staging:
git add -p src/controllers/user.js
Isso abre um modo interativo onde você decide, hunck por hunck, o que incluir no próximo commit.
Exemplo: commit atômico vs. monolítico
Commit monolítico (ruim):
feat: atualiza módulo de vendas
Este commit pode conter: correção de bug, nova funcionalidade, refatoração e mudança de estilo — tudo junto.
Commits atômicos (bom):
fix: corrige validação de CPF no formulário de vendas
feat: adiciona campo de desconto progressivo
refactor: extrai lógica de cálculo de impostos
style: ajusta padding dos botões do carrinho
5. Reescrevendo o histórico: squash, rebase e amend
git commit --amend
Corrige a mensagem do último commit ou adiciona mudanças esquecidas:
git commit --amend -m "feat: nova mensagem corrigida"
git rebase -i (rebase interativo)
Permite combinar, reordenar ou editar commits. Para squashear os últimos 3 commits:
git rebase -i HEAD~3
No editor que abrir, substitua pick por squash nos commits que deseja combinar:
pick a1b2c3 feat: implementa login básico
squash d4e5f6 feat: adiciona validação de email
squash g7h8i9 feat: ajusta fluxo de erro
Cuidados importantes
Nunca reescreva histórico de branches públicas (main, develop) compartilhadas com outros desenvolvedores. Use rebase apenas em branches locais ou de feature.
6. Estratégias de branching e merge que preservam a legibilidade
Merge commits vs. rebase
Merge commits criam um nó de merge visível no gráfico, preservando o contexto de que mudanças vieram de branches diferentes:
git checkout main
git merge feature/login
Rebase mantém o histórico linear, reaplicando os commits da branch de feature sobre a branch base:
git checkout feature/login
git rebase main
git checkout main
git merge feature/login
git merge --squash
Combina todos os commits de uma branch em um único commit antes de mergear:
git checkout main
git merge --squash feature/login
git commit -m "feat: implementa sistema de login completo"
Manter o histórico linear
A abordagem recomendada para equipes é: faça rebase da branch de feature antes do merge, resultando em um histórico linear e limpo.
7. Ferramentas e boas práticas complementares
Hooks de commit com linters
Commitlint valida mensagens de commit contra regras configuradas. Instalação com Husky:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Configure o hook no Husky:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
Integração com CI/CD
Valide automaticamente as mensagens de commit no pipeline de CI:
# .github/workflows/commitlint.yml
name: Commit Lint
on: [push, pull_request]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: wagoid/commitlint-github-action@v5
Templates de commit
Crie um template para padronizar mensagens no time:
# .gitmessage.txt
<tipo>(<escopo>): <descrição>
<corpo>
<rodapé>
Configure o template:
git config commit.template .gitmessage.txt
8. Lidando com erros comuns no histórico
git revert para desfazer commits
Diferente de reset, revert cria um novo commit que desfaz as mudanças, preservando o histórico:
git revert HEAD
git reflog para recuperar commits perdidos
Se você fez um reset ou rebase que removeu commits, o reflog mostra o histórico de movimentos do HEAD:
git reflog
# Encontre o hash do commit desejado
git checkout <hash>
Corrigir mensagens de commits antigos
Use git rebase -i e marque o commit como reword:
git rebase -i HEAD~5
# Altere 'pick' para 'reword' no commit desejado
Isso abrirá um editor para cada commit marcado, permitindo corrigir a mensagem.
Manter um histórico de commits limpo é um investimento que se paga inúmeras vezes ao longo da vida de um projeto. Comece aplicando uma convenção como Conventional Commits, pratique commits atômicos e use ferramentas automatizadas para garantir consistência. Sua equipe — e seu eu do futuro — agradecerão.
Referências
- Conventional Commits 1.0.0 — Especificação oficial do padrão Conventional Commits, com exemplos e tipos recomendados.
- Git - Documentação Oficial — Capítulo sobre reescrita de histórico no Pro Git Book, abordando amend, rebase e reflog.
- Commitlint — Ferramenta para validar mensagens de commit com regras configuráveis, integrada a hooks Git.
- Husky — Gerenciador de hooks Git que facilita a automação de validações antes de commits e pushes.
- Semantic Versioning 2.0.0 — Especificação oficial do versionamento semântico, base para integração com Conventional Commits.
- Conventional Changelog — Gerador de changelogs baseado em commits padronizados, usado em automação de releases.
- Git - git-revert Documentation — Documentação oficial do comando git-revert para desfazer commits com segurança.