W3docs

Git hooks

Aprenda Git hooks — scripts que rodam automaticamente no ciclo de vida do Git para linting, testes e validação. Inclui exemplo de pre-commit.

O que são Git hooks

Git hooks são scripts que o Git executa automaticamente quando determinados eventos ocorrem — commit, merge, push e outros. Eles permitem adicionar ações personalizadas ao ciclo de vida do Git: executar um linter antes de cada commit, validar o formato de uma mensagem de commit ou bloquear um push se os testes falharem. Hooks são a forma como as equipes impõem portões de qualidade localmente, antes que código ruim saia de uma máquina.

Esta página aborda onde os hooks ficam armazenados, a diferença entre hooks client-side e server-side, os hooks mais úteis com exemplos funcionais, como um hook aborta uma operação, como contornar um hook e como compartilhar hooks com uma equipe.

Git hooks disparando nos estágios do ciclo de vida de commit e push

Onde os hooks ficam armazenados

Todo repositório possui um diretório .git/hooks com scripts de exemplo com o sufixo .sample. Para ativar um hook, adicione um script executável com o nome exato do hook e sem extensão:

ls .git/hooks
# pre-commit.sample  commit-msg.sample  pre-push.sample ...

Remova o sufixo .sample (ou crie o arquivo do zero) e torne-o executável:

chmod +x .git/hooks/pre-commit

Um hook pode ser escrito em qualquer linguagem, desde que o arquivo seja executável e comece com uma linha shebang adequada (#!/bin/sh, #!/usr/bin/env python3, #!/usr/bin/env node, etc.). O Git só se importa que o arquivo tenha o nome exato de um hook conhecido, seja executável e retorne um código de saída.

Hooks client-side vs server-side

  • Hooks client-side rodam na sua máquina em operações locais como commit e push. São ótimos para linting e testes.
  • Hooks server-side (como pre-receive e post-receive) rodam no repositório remoto quando ele recebe um push — úteis para impor políticas de forma centralizada.

Os hooks mais usados são client-side:

HookDisparaUso típico
pre-commitAntes de um commit ser criadoLint e teste de arquivos staged; abortar em caso de falha.
prepare-commit-msgAntes do editor de mensagem abrirInserir um template ou número de ticket.
commit-msgApós a mensagem ser escritaImpor uma convenção de mensagem.
post-commitApós um commit ser concluídoEnviar uma notificação; sem efeito sobre o commit.
pre-pushAntes de um push ser enviadoExecutar a suite de testes completa como portão final.

Como um hook aborta uma operação

Todo o mecanismo de controle é o código de saída. Para um hook "pre-" (pre-commit, pre-push, commit-msg, …):

  • Saída 0 → o hook aprovou a operação e o Git continua.
  • Saída diferente de zero → o Git cancela a operação. O commit não é criado ou o push não é enviado.

Hooks "post-" (post-commit, post-merge, …) rodam após a ação já ter sido concluída, então seu código de saída é ignorado — eles não podem desfazer nada. Use-os para notificações, não para validação.

Um exemplo de pre-commit

Este hook pre-commit executa o linter do projeto e bloqueia o commit se ele reportar problemas. Como o sh aborta no comando com falha graças ao set -e, não é necessário verificar $? manualmente:

#!/bin/sh
set -e
echo "Running lint..."
npm run lint

Se npm run lint sair com código diferente de zero, o set -e propagará esse código e o commit será abortado. Se preferir uma mensagem personalizada, verifique o resultado explicitamente:

#!/bin/sh
if ! npm run lint; then
  echo "Lint failed — commit aborted. Fix the issues and try again."
  exit 1
fi

Salve isso como .git/hooks/pre-commit e execute chmod +x .git/hooks/pre-commit.

Um exemplo de commit-msg

O hook commit-msg recebe um argumento: o caminho para um arquivo temporário contendo a mensagem proposta. Leia esse arquivo, valide-o e saia com código diferente de zero para rejeitar. Este exemplo impõe um prefixo no estilo Conventional Commits:

#!/bin/sh
# $1 is the path to the file containing the commit message
message=$(head -n1 "$1")
pattern='^(feat|fix|docs|style|refactor|test|chore): .+'

if ! echo "$message" | grep -Eq "$pattern"; then
  echo "Commit message must start with feat:, fix:, docs:, etc."
  exit 1
fi

Contornando um hook

Um hook é uma rede de segurança, não uma barreira. Quando você genuinamente precisa pular os hooks pre-commit e commit-msg em uma operação, use --no-verify:

git commit --no-verify -m "WIP: skip checks"
git push --no-verify

Use isso com moderação — contornar o linter é como código quebrado escorrega para o histórico.

Compartilhando hooks com uma equipe

Como .git/hooks não é commitado, os hooks não viajam com um clone. As equipes resolvem isso armazenando hooks em um diretório rastreado e apontando o Git para ele:

git config core.hooksPath .githooks

Agora o Git procura no diretório rastreado .githooks/ em vez de .git/hooks. Faça commit dos seus scripts lá, marque-os como executáveis e todos os membros da equipe os receberão após executar o mesmo comando git config (ou após um script de configuração fazer isso por eles). Veja Git config para mais informações sobre como armazenar configurações por repositório.

Ferramentas como Husky automatizam exatamente isso para projetos JavaScript, conectando hooks compartilhados durante a instalação. Para políticas que você não pode deixar ninguém contornar com --no-verify, imponha-as com hooks server-side ou a proteção de branches da sua plataforma de hospedagem, pois hooks client-side sempre residem na máquina do desenvolvedor.

Tópicos relacionados

  • git commit — o comando em torno do qual os hooks pre-commit e commit-msg funcionam.
  • Assinando commits — verificar autoria, frequentemente combinado com hooks.
  • Git config — onde core.hooksPath e outras configurações são armazenadas.
  • Git alias — atalhos para os comandos que seus hooks executam.

Prática

Prática
Quais afirmações sobre Git hooks são corretas?
Quais afirmações sobre Git hooks são corretas?
Was this page helpful?