Chapters ▾ 2nd Edition

8.3 Customizando o Git - Hooks do Git

Hooks do Git

Como muitos outros Sistemas de Controle de Versão, o Git tem uma maneira de disparar scripts customizados quando certas ações importantes ocorrem. Existem dois grupos desses hooks: os do lado do cliente (client-side) e os do lado do servidor (server-side). Hooks do lado do cliente são acionados por operações como fazer commits e merges, enquanto os hooks do lado do servidor rodam em operações de rede, como o recebimento de commits que sofreram push. Você pode usar esses hooks por toda a sorte de motivos.

Instalando um Hook

Todos os hooks são armazenados no subdiretório hooks do diretório Git. Na maior parte dos projetos, trata-se do .git/hooks. Quando você inicializa um novo repositório com git init, o Git popula o diretório hooks com um monte de scripts de exemplo, muitos dos quais chegam a ser úteis por si mesmos; mas eles também documentam os valores de entrada de cada script em si. Todos os exemplos são escritos como shell scripts, com algum Perl inserido, mas quaisquer scripts executáveis desde que devidamente nomeados funcionarão muito bem – você pode programá-los em Ruby ou Python ou em qualquer linguagem com a qual tenha familiaridade. Se você desejar utilizar os scripts de hook empacotados, você terá que renomeá-los; os nomes de arquivo de todos eles terminam com .sample.

Para habilitar um script de hook, coloque um arquivo no subdiretório hooks do seu diretório .git que seja nomeado adequadamente (sem nenhuma extensão) e seja executável. Desse ponto em diante, ele deverá ser chamado. Abordaremos a maioria dos nomes de arquivo dos principais hooks aqui.

Hooks do Lado do Cliente (Client-Side)

Há muitos hooks do lado do cliente. Esta seção os divide em hooks do fluxo de trabalho de commit, scripts do fluxo de trabalho de e-mail e tudo o mais.

Note

É importante notar que os hooks do lado do cliente não são copiados quando você clona um repositório. Se a sua intenção com esses scripts é impor uma política, você provavelmente vai querer fazer isso no lado do servidor; veja o exemplo em Uma Política de Exemplo Imposta pelo Git.

Hooks de Fluxo de Trabalho de Commit

Os primeiros quatro hooks têm a ver com o processo de commit.

O hook pre-commit é executado primeiro, antes mesmo de você digitar uma mensagem de commit. Ele é usado para inspecionar o snapshot que está prestes a sofrer o commit, para ver se você esqueceu algo, para ter a certeza de que os testes rodam ou para examinar o que quer que você precise inspecionar no código. Se você sair com um status diferente de zero (non-zero) deste hook, aborta-se o commit, embora você possa contorná-lo com git commit --no-verify. Você pode fazer coisas como checar o estilo do código (rodar o lint ou algo equivalente), checar espaços em branco à direita (trailing whitespace - o hook padrão faz exatamente isso) ou verificar se há uma documentação apropriada nos novos métodos.

O hook prepare-commit-msg é rodado antes que o editor de mensagem de commit seja iniciado, mas depois que a mensagem padrão é criada. Ele permite que você edite a mensagem padrão antes que o autor do commit a veja. Esse hook aceita alguns parâmetros: o caminho para o arquivo que contém a mensagem de commit até o momento, o tipo de commit e o SHA-1 do commit, se este for um commit alterado (amended commit). Esse hook geralmente não é útil para commits normais; em vez disso, é bom para commits onde a mensagem padrão é gerada automaticamente, como em mensagens de commit de templates, commits de merge, commits esmagados (squashed commits) e commits alterados (amended commits). Você pode usá-lo em conjunto com um template de commit para inserir informações programaticamente.

O hook commit-msg aceita um parâmetro, que é novamente o caminho para um arquivo temporário que contém a mensagem de commit escrita pelo desenvolvedor. Se esse script sair com status não-zero (non-zero), o Git aborta o processo de commit, portanto, você pode usá-lo para validar o estado do seu projeto ou a mensagem de commit antes de permitir que um commit seja concluído. Na última seção deste capítulo, nós demonstraremos o uso deste hook para checar se a sua mensagem de commit está em conformidade com um padrão exigido.

Depois que todo o processo de commit é completado, o hook post-commit roda. Ele não recebe nenhum parâmetro, mas você pode facilmente obter o último commit rodando git log -1 HEAD. Geralmente, esse script é usado para notificação ou algo parecido.

Hooks do Fluxo de Trabalho de E-mail

Você pode configurar três hooks do lado do cliente para um fluxo de trabalho baseado em e-mail. Todos eles são invocados pelo comando git am, portanto, se você não está usando esse comando no seu fluxo de trabalho, você pode pular tranquilamente para a próxima seção. Se você estiver pegando patches por e-mail preparados por git format-patch, então alguns destes podem lhe ser úteis.

O primeiro hook que é rodado é o applypatch-msg. Ele recebe um único argumento: o nome do arquivo temporário que contém a mensagem de commit proposta. O Git aborta o patch se este script sair com status diferente de zero (non-zero). Você pode usá-lo para ter a certeza de que uma mensagem de commit está devidamente formatada ou para normalizar a mensagem fazendo com que o script a edite ali no local (in place).

O próximo hook a rodar quando se aplica patches via git am é o pre-applypatch. De uma forma um tanto confusa, ele roda após o patch ser aplicado, mas antes que um commit seja feito, então você pode usá-lo para inspecionar o snapshot antes de fazer o commit. Você pode rodar testes ou de alguma outra forma inspecionar a árvore de trabalho com este script. Se algo estiver faltando ou se os testes não passarem, a saída num não-zero aborta o script do git am sem fazer o commit do patch.

O último hook a rodar durante uma operação git am é o post-applypatch, que roda após o commit ser feito. Você pode usá-lo para notificar um grupo ou o autor do patch do qual você fez pull de que você fez isso. Você não tem como parar o processo de patch com este script.

Outros Hooks de Cliente

O hook pre-rebase roda antes de você fazer o rebase de qualquer coisa e pode interromper o processo saindo com um não-zero (non-zero). Você pode usar este hook para não permitir o rebase de quaisquer commits que já tenham sido alvo de push. O hook pre-rebase de exemplo que o Git instala faz isso, embora ele faça algumas suposições que podem não bater (match) com o seu fluxo de trabalho.

O hook post-rewrite é rodado por comandos que substituem commits, como git commit --amend e git rebase (embora não pelo git filter-branch). Seu argumento único é qual comando engatilhou (triggered) a reescrita, e ele recebe uma lista de reescritas no stdin. Este hook tem muitos dos mesmos usos que os hooks post-checkout e post-merge.

Após você rodar um git checkout com sucesso, o hook post-checkout roda; você pode usá-lo para configurar apropriadamente seu diretório de trabalho para o ambiente do seu projeto. Isso pode significar mover arquivos binários grandes que você não quer que passem por controle de versão no código (source controlled), documentação gerada automaticamente ou algo nessa linha.

O hook post-merge roda após um comando merge bem-sucedido. Você pode usá-lo para restaurar dados na árvore de trabalho que o Git não pode rastrear, tais como dados de permissões. Este hook também (likewise) pode validar a presença de arquivos externos ao controle do Git que você possa querer copiar quando a árvore de trabalho mudar.

O hook pre-push roda durante o git push, após as refs do remoto terem sido atualizadas, mas antes de quaisquer objetos terem sido transferidos. Ele recebe o nome e o local do remoto como parâmetros e uma lista de refs que estão para serem atualizadas através da stdin. Você pode usá-lo para validar um conjunto de atualizações de ref antes que um push ocorra (um código de saída num despontar não-zero abortará o push).

O Git ocasionalmente faz coleta de lixo (garbage collection) como parte do seu funcionamento normal, invocando git gc --auto. O hook pre-auto-gc é invocado logo antes que a coleta de lixo ocorra, e pode ser usado para notificá-lo de que isso está acontecendo, ou para abortar a coleta se agora não for um bom momento.

Hooks do Lado do Servidor

Além dos hooks do lado do cliente, você pode usar dois (a couple of) hooks importantes do lado do servidor como um administrador de sistema para impor (enforce) quase qualquer tipo de política no seu projeto. Esses scripts rodam antes e depois dos pushes para o servidor. Os hooks de pré (pre hooks) podem sair num despontar não-zero (exit non-zero) a qualquer momento para rejeitar o push, bem como imprimir uma mensagem de erro de volta para o cliente; você pode configurar uma política de push que seja tão complexa quanto desejar.

pre-receive

O primeiro script a rodar ao lidar com um push de um cliente é o pre-receive. Ele pega (takes) uma lista das referências que estão sofrendo push vindas da stdin; se ele sair com não-zero (non-zero), nenhuma delas será aceita. Você pode usar este hook para fazer coisas como garantir que nenhuma das referências atualizadas seja non-fast-forward, ou para fazer controle de acesso para todas as refs e arquivos que eles estejam a modificar com o push.

update

O script update é muito semelhante ao script pre-receive, exceto que ele é rodado uma vez para cada branch que o encarregado pelo push (pusher) esteja tentando atualizar. Se o pusher estiver tentando fazer o push para várias branches, o pre-receive roda somente uma vez, enquanto que o update roda uma vez por branch para a qual eles estejam fazendo o push. Em vez de ler do stdin, este script aceita (takes) três argumentos: o nome da referência (branch), o SHA-1 para onde a referência apontava antes do push, e o SHA-1 que o usuário está tentando fazer o push. Se o script update sair com não-zero, somente aquela referência é rejeitada; as outras referências ainda podem ser atualizadas.

post-receive

O hook post-receive roda depois que todo o processo for concluído e pode ser usado para atualizar outros serviços ou notificar os usuários. Ele aceita os mesmos dados pelo stdin que o hook pre-receive. Exemplos disso incluem mandar um e-mail para uma lista, notificar um servidor de integração contínua (continuous integration) ou atualizar um sistema de rastreamento de tickets (ticket-tracking system) – você pode até mesmo analisar (parse) as mensagens de commit para ver se quaisquer tickets precisam vir a ser abertos, modificados ou fechados. Este script não tem como parar o processo de push, mas o cliente não é desconectado até que ele tenha sido concluído; portanto, tenha cuidado se você tentar fazer qualquer coisa que possa tomar muito tempo.

Tip

Se você estiver escrevendo um script/hook que outros precisarão ler, dê preferência às versões mais longas das flags de linha de comando; daqui a seis meses você irá nos agradecer por isso.