Também disponível em: English · Español · Français · العربية
Git hooks: a lista completa e quais podem te barrar
O conjunto completo que o git documenta, e a resposta medida para a única questão que importa: este hook consegue interromper o que você está fazendo?
O que é um hook do git?
Um hook do git é um arquivo executável dentro do diretório de hooks do seu repositório, nomeado a partir de um momento da vida do git. Quando esse momento chega — você vai fazer um commit, acabou de mesclar, um push chegou ao servidor — o git executa o arquivo. É assim que um projeto impõe um formato de mensagem de commit, roda um linter antes de o código entrar ou recusa um push forçado à branch principal.
O git documenta 28 deles. A maioria das pessoas encontra três ou quatro. O resto cobre a aplicação de patches por e-mail, o rebase, a ponta que recebe um push, a coleta de lixo e uma ponte para o Perforce, e vários rodam em lugares que ninguém adivinharia pelo nome.
A pergunta com que qualquer um chega é simples: se este hook falhar, ele me barra? Toda referência que encontramos responde copiando a lista do manual do git. Esta página responde executando-os.
Como usar
- Digite o nome de um hook. Você recebe onde ele roda, o que a documentação do git diz sobre o status de saída dele e o que aconteceu quando ele foi de fato instalado e mandado falhar.
- Ou busque no conjunto inteiro. A tabela abaixo tem os 28. Buscar um comando do git — commit, push, git am — traz todos os hooks que disparam durante ele.
- Leia as duas colunas uma contra a outra. Elas vêm de lugares diferentes, então onde coincidem você pode confiar na resposta, e onde a documentação não tem palavra para o que acontece, a medição fornece uma.
O que aconteceu quando os executamos
Cada hook foi instalado sozinho, num repositório descartável com um remoto bare local ao lado, com um corpo que não fazia nada além de falhar. Depois a operação a que esse hook pertence foi realizada, e duas coisas foram registradas: o código de saída que o comando devolveu e se o estado do repositório de fato se moveu. Dezenove dos 28 podem ser acionados assim; os demais precisam de um depósito Perforce, um transporte de e-mail, o watchman ou o protocolo de push mais novo, e ficam em branco em vez de chutados.
Cada caso também rodou sem hook algum instalado, e esse controle precisava dar certo e mover o estado antes de a medição valer qualquer coisa. Não é cerimônia. A primeira versão deste experimento relatou que o hook pre-rebase não consegue interromper um rebase — o que é falso, e aconteceu porque a branch que ele montava não tinha nada a rebasear, então a operação não fazia nada havendo hook ou não. O controle transformou uma falsidade plausível e publicável numa falha barulhenta.
Dos dezenove: onze interromperam a operação, sete não mudaram absolutamente nada, e um fez algo para o qual não existe palavra comum.
O hook que falha sem falhar
O hook post-checkout não consegue interromper um checkout. A própria documentação do git diz que ele não pode afetar o resultado — exceto que o status de saída dele vira o status de saída do comando. Medido, é exatamente isso que acontece: o checkout se completa, você está na branch nova, e o git devolve 1.
Então um script escrito como um checkout seguido de um comando encadeado, que é como quase todo script de deploy e passo de CI é escrito, para seco num repositório cujo hook post-checkout falha. O checkout funcionou. O comando falhou. São fatos diferentes e só um deles é visível para o shell.
Vale conhecer mais um código de saída. Quando o hook reference-transaction recusa no estado prepared, o git não devolve uma falha comum — devolve 128, o código dele para erro fatal. Qualquer coisa que compare códigos de saída deveria esperar isso em vez de 1.
O nome não é a regra
Todo mundo aprende assim: hooks que começam com pre- podem barrar, hooks que começam com post- não. Metade está certa. Nada chamado post- interrompeu coisa alguma, e todo hook que não mudou absolutamente nada se chamava post-alguma-coisa.
A outra metade falha. Só 6 dos 11 hooks que interromperam uma operação se chamam pre-alguma-coisa. Os outros cinco são applypatch-msg, commit-msg, prepare-commit-msg, update e reference-transaction — ou seja, quase metade do que pode te barrar não avisa no nome. prepare-commit-msg é a armadilha dentro da armadilha: começa com as letras p-r-e, não é um hook pre-, e barra.
A regra de verdade é se o git consulta o status de saída no ponto em que o hook roda, o que é uma propriedade da operação e não do nome. É por isso que update interrompe um push, que reference-transaction pode impedir a criação de uma branch, e que post-checkout consegue fazer um comando falhar sem mudar nada.
O que esta página não consegue dizer
É uma versão do git numa plataforma. A medição foi tomada com a versão do git impressa ao lado dos números, no Windows, e embora a semântica dos hooks mude devagar, ela muda. Onde a documentação e a medição concordam — que é em tudo que se sobrepõe — a resposta é tão sólida quanto dá sem ler o código-fonte.
Nove hooks não foram medidos, e a linha deles diz isso em vez de ser preenchida. Um deles, pre-auto-gc, foi tentado e abandonado: a coleta automática de lixo só roda depois que o git decide que um repositório tem objetos soltos suficientes, e essa decisão amostra um diretório em vez de contar, então nenhum repositório de teste pequeno o bastante a disparava. Uma linha cujo controle não pode ser feito honesto fica de fora.
Por fim, isto trata de se um hook consegue interromper uma operação, não de se deveria. Um hook que recusa trabalho só serve se a pessoa barrada souber por quê, e o git não imprime nada em seu nome: o que o seu hook escrever na saída de erro é a mensagem inteira que qualquer um verá.
Por que é grátis?
A lista inteira são uns poucos kilobytes que viajam com a página e são pesquisados no seu navegador. Nada do que você digita é enviado, nada é registrado e não há conta a criar.
Sem cadastro, sem limites e sem marca d'água em nada que você copiar.