Conventional Commits e SemVer
Boas mensagens de commit são a diferença entre um histórico Git que conta a história do projeto e um que apenas acumula entradas sem sentido. Esta página cobre o padrão Conventional Commits, o versionamento semântico que ele sustenta e as ferramentas que automatizam o processo no dia a dia.
Versionamento Semântico (SemVer)
Seção intitulada “Versionamento Semântico (SemVer)”Toda biblioteca, API ou software que outras pessoas dependem precisa de um sistema de versão previsível. O SemVer (Semantic Versioning) define um padrão amplamente adotado: o número de versão comunica o tipo de mudança, não apenas “houve mudança”.
O formato é sempre:
MAJOR.MINOR.PATCHExemplo: 2.4.1
| Número | Quando incrementar | O que significa para quem usa |
|---|---|---|
| MAJOR | Mudança que quebra compatibilidade com versões anteriores | É preciso adaptar o código para atualizar |
| MINOR | Nova funcionalidade sem quebrar o que já existia | Pode atualizar com segurança |
| PATCH | Correção de bug sem mudança de comportamento | Deve atualizar - sem risco |
Regras práticas:
- Quando MAJOR incrementa, MINOR e PATCH voltam a 0
- Quando MINOR incrementa, PATCH volta a 0
- Versões
0.x.xindicam desenvolvimento inicial - a API pode mudar a qualquer momento - Versão
1.0.0marca o ponto em que a API pública está estável
Versões de pré-release:
1.0.0-alpha1.0.0-beta.11.0.0-rc.1 # release candidateNa prática - npm e semver:
Quando você vê "react": "^18.2.0" no package.json, o ^ significa “aceita MINOR e PATCH novos, mas não MAJOR”. Isso vem diretamente do SemVer: uma versão MINOR não quebra compatibilidade, então é seguro atualizar automaticamente.
Spec completa em português: semver.org/lang/pt-BR
Conventional Commits
Seção intitulada “Conventional Commits”O Conventional Commits é uma especificação para mensagens de commit que torna o histórico legível por humanos e por ferramentas. Git: Fundamentos cobre os prefixos básicos - aqui vamos ao formato completo e às ferramentas que automatizam o processo.
Formato:
<tipo>[escopo opcional]: <descrição curta>
[corpo opcional]
[rodapé(s) opcional(is)]Escopo:
Indica qual parte do sistema foi afetada. Vai entre parênteses depois do tipo:
feat(auth): adicionar login com Google OAuthfix(carrinho): corrigir arredondamento de fretedocs(readme): atualizar instruções de instalaçãoCorpo e rodapé:
Para commits mais complexos, use o corpo para explicar o porquê e o rodapé para metadados como referências a issues ou revisores:
feat(auth): adicionar login com Google OAuth
Implementa o fluxo OAuth 2.0 com Google. Usuários podemagora fazer login sem criar uma senha local.
Closes #42Reviewed-by: @colegaBreaking changes:
Quando a mudança quebra compatibilidade (e deve incrementar o MAJOR no SemVer), sinalize de uma dessas duas formas:
# Opção 1: ! depois do tipofeat!: remover suporte ao endpoint /api/v1
# Opção 2: rodapé BREAKING CHANGEfeat(api): nova estrutura de resposta nos endpoints
BREAKING CHANGE: O campo "data" foi renomeado para "payload".Clientes precisam atualizar as chamadas para usar o novo campo.A relação com SemVer:
Conventional Commits foi projetado para trabalhar junto com SemVer. A regra é direta:
fix:→ incrementa PATCHfeat:→ incrementa MINORfeat!:ouBREAKING CHANGE:→ incrementa MAJOR
Ferramentas como o semantic-release leem o histórico de commits e calculam automaticamente qual versão deve ser lançada - sem intervenção manual.
commitlint
Seção intitulada “commitlint”O commitlint valida suas mensagens de commit automaticamente. Se você escrever algo fora do padrão Conventional Commits, o commit é bloqueado antes de ser criado.
Instalação em projetos Node.js:
npm install --save-dev @commitlint/cli @commitlint/config-conventionalConfiguração - crie commitlint.config.js na raiz do projeto:
export default { extends: ["@commitlint/config-conventional"],};Integrando com Husky (executa a validação automaticamente antes de cada commit):
npm install --save-dev huskynpx husky initecho "npx --no -- commitlint --edit \$1" > .husky/commit-msgO arquivo .husky/commit-msg é executado pelo Git antes de cada commit. Se a mensagem não passar na validação, o commit não acontece.
Exemplo de erro:
$ git commit -m "arrumei o bug"⧗ input: arrumei o bug✖ subject may not be empty [subject-empty]✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warningsExemplo de commit válido:
$ git commit -m "fix: corrigir cálculo de desconto no carrinho"[main a1b2c3d] fix: corrigir cálculo de desconto no carrinhoVocê pode personalizar as regras no commitlint.config.js para aceitar escopos específicos, exigir rodapé de issue, limitar o tamanho da descrição, etc.
commitizen
Seção intitulada “commitizen”O commitizen é uma CLI interativa que guia você pelo processo de criar uma mensagem no padrão Conventional Commits. Em vez de lembrar a sintaxe, você responde a perguntas e ele monta a mensagem.
Instalação:
npm install --save-dev commitizen cz-conventional-changelogConfiguração - adicione ao package.json:
{ "config": { "commitizen": { "path": "cz-conventional-changelog" } }}Adicione também um script para facilitar o uso:
{ "scripts": { "commit": "cz" }}Uso:
npm run commitO commitizen faz uma série de perguntas no terminal:
? Select the type of change you're committing:❯ feat: A new feature fix: A bug fix docs: Documentation only changes ...
? What is the scope of this change? (enter to skip) auth
? Write a short, imperative tense description: adicionar login com Google
? Provide a longer description: (press enter to skip)
? Are there any breaking changes? No
? Does this change affect any open issues? Closes #42E gera a mensagem pronta:
feat(auth): adicionar login com Google
Closes #42Usando os dois juntos:
commitlint + commitizen é a configuração mais robusta para times. O commitizen garante que ninguém escreve mensagens fora do padrão por não conhecer o formato. O commitlint é a rede de segurança que bloqueia mensagens inválidas mesmo que alguém use git commit diretamente, ignorando o commitizen.