Les hooks Git sont des scripts qui s'exécutent automatiquement dès qu'un événement particulier se produit dans un dépôt Git. Ils vous permettent de personnaliser le comportement interne de Git et de déclencher des actions personnalisables à des points clés dans le cycle de vie du développement.

Hooks s'exécutant au cours du process de création de commits

Quelques utilisations courantes des hooks Git : l'encouragement d'une stratégie de commit, la modification de l'environnement du projet en fonction de l'état du dépôt et l'implémentation de workflows d'intégration continue. Mais, dans la mesure où les scripts sont personnalisables à l'infini, vous pouvez utiliser les hooks Git pour automatiser ou optimiser pratiquement tout votre workflow de développement.

Cet article débutera par une présentation conceptuelle du fonctionnement des hooks Git. Nous étudierons ensuite quelques hooks parmi les plus populaires qui sont utilisés dans des dépôts locaux et côté serveur.

Présentation des concepts

Tous les hooks Git sont des scripts ordinaires que Git exécute lorsqu'un événement spécifique se produit dans le dépôt. Ils sont donc très simples à installer et à configurer.

Les hooks peuvent se trouver dans des dépôts locaux ou côté serveur et ils ne sont exécutés qu'en réponse à des actions dans le dépôt en question. Nous aborderons concrètement les catégories de hooks plus loin dans cet article. La configuration étudiée dans le reste de cette section s'applique aux hooks locaux et côté serveur.

Installer des hooks

Les hooks résident tous dans le répertoire .git/hooks de chaque dépôt Git. Par défaut, Git remplit ce répertoire avec des exemples de script lorsque vous initialisez un dépôt. Si vous jetez un œil dans .git/hooks, vous y trouverez les fichiers suivants :

applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
post-update.sample prepare-commit-msg.sample
pre-applypatch.sample update.sample
pre-commit.sample

Ceux-ci représentent la plupart des hooks disponibles. L'extension .sample empêche leur exécution par défaut. Pour installer un hook, il vous suffit de supprimer l'extension .sample. Ou, si vous écrivez un nouveau script à partir de zéro, ajoutez simplement un nouveau fichier correspondant à l'un des noms de fichier ci-dessus, sans l'extension .sample.

À titre d'exemple, essayez d'installer un hook prepare-commit-msg classique. Supprimez l'extension .sample du script, puis ajoutez la ligne suivante au fichier :

#!/bin/sh

echo "# Merci d'inclure un message de commit utile !" > $1

Les hooks doivent être exécutables, c'est pourquoi vous devrez sans doute modifier les permissions de fichier du script si vous le créez intégralement. Par exemple, pour veiller à ce que prepare-commit-msg soit exécutable, lancez la commande suivante :

chmod +x prepare-commit-msg

Ce message devrait désormais s'afficher à la place du message de commit par défaut à chaque fois que vous lancez git commit. Nous examinerons ce système de plus près dans la section Préparer un message de commit. Pour le moment, contentons-nous de pouvoir personnaliser certaines fonctionnalités internes de Git.

Les exemples de scripts intégrés sont des références très pratiques, puisqu'ils documentent les paramètres qui sont transmis à chaque hook (ils varient en fonction des hooks).

Langages de script

La plupart des scripts intégrés sont des scripts Shell ou Perl, mais vous pouvez utiliser n'importe quel langage de script tant que le fichier est exécutable. La ligne shebang (#!/bin/sh) contenue dans chaque script détermine la manière dont votre fichier doit être interprété. Ainsi, si vous voulez utiliser un autre langage, il vous suffit de la remplacer par le chemin d'accès à votre interpréteur.

Par exemple, vous pouvez écrire un script Python exécutable dans le fichier prepare-commit-msg au lieu d'utiliser les commandes Shell. Le hook suivant se comportera de la même manière que le script Shell dont il était question dans la section précédente.

#!/usr/bin/env python
import sys, os
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'w') as f:
f.write("# Veuillez inclure un message de commit utile !")

Remarque : la première ligne pointe maintenant vers l'interpréteur Python. Au lieu d'utiliser $1 pour accéder au premier argument du script, nous avons utilisé sys.argv[1] (nous y reviendrons également dans un instant).

C'est une fonctionnalité très puissante pour les hooks Git, car elle vous permet de travailler dans le langage avec lequel vous êtes le plus à l'aise.

Portée des hooks

Les hooks sont stockés en local dans un dépôt Git donné et ne sont pas copiés dans le nouveau dépôt lorsque vous exécutez git clone. Comme les hooks sont stockés en local, tout utilisateur qui accède au dépôt peut les modifier.

Cette caractéristique a un impact important lors de la configuration des hooks pour une équipe de développeurs. Premièrement, vous devez trouver un moyen pour vous assurer que les hooks restent à jour entre tous les membres de votre équipe. Deuxièmement, vous ne pouvez pas forcer les développeurs à créer des commits ayant une apparence spécifique. Vous pouvez seulement les y encourager.

La gestion des hooks peut s'avérer délicate pour une équipe de développeurs, car le répertoire .git/hooks n'a pas été cloné avec le reste du projet et n'est pas non plus inclus dans le contrôle de version. Pour régler ces deux problèmes, une solution très simple s'offre à vous : il vous suffit de stocker les hooks dans le répertoire de projet courant (ci-dessus le répertoire .git). Ainsi, vous pourrez les modifier comme tout autre fichier sous contrôle de version. Pour installer un hook, vous pouvez créer un lien symbolique vers celui-ci dans .git/hooks ou simplement le copier puis le coller dans le répertoire .git/hooks à chaque fois que le hook est mis à jour.

Hooks s'exécutant au cours du process de création de commits

Parallèlement, Git propose également un système de modèle de répertoire qui facilite l'installation automatique des hooks. Tous les fichiers et les répertoires contenus dans ce modèle de répertoire sont copiés dans le répertoire .git à chaque fois que vous utilisez git init ou git clone.

Tous les hooks locaux décrits ci-dessous peuvent être modifiés ou totalement désinstallés par le propriétaire d'un dépôt. Chaque membre de l'équipe est entièrement libre de choisir d'utiliser un hook ou non. Les hooks Git doivent donc être considérés comme un outil de développement pratique plutôt que comme une stratégie de développement imposée.

Cela dit, il est possible de rejeter les commits qui ne sont pas conformes à une norme en utilisant des hooks côté serveur. Nous aborderons ce point plus tard dans cet article.

Hooks locaux

Les hooks locaux affectent uniquement le dépôt dans lequel ils se trouvent. Lorsque vous lirez cette section, gardez à l'esprit que chaque développeur peut modifier ses hooks locaux. Par conséquent, vous ne pouvez pas les utiliser pour appliquer une stratégie de commit. Ils peuvent toutefois grandement contribuer au respect de certaines recommandations.

Cette section portera sur six des hooks locaux les plus utiles :

  • Pré-commit
  • prepare-commit-msg
  • commit-msg
  • Post-commit
  • Post-checkout
  • Pre-rebase

Les quatre premiers hooks vous donnent accès au cycle de vie complet des commits, et les deux derniers vous permettent d'effectuer quelques actions ou des contrôles de sécurité supplémentaires respectivement pour les commandes git checkout et git rebase.

Tous les hooks pre- vous permettent de modifier l'action qui est sur le point d'avoir lieu, tandis que les hooks post- sont uniquement utilisés à des fins de notification.

Nous verrons également quelques techniques utiles pour analyser les arguments de hook et demander des informations sur le dépôt à l'aide de commandes Git de niveau inférieur.

Pré-commit

Le script pre-commit s'exécute à chaque fois que vous lancez git commit, avant que Git ne demande au développeur d'entrer un message de commit ou ne crée un objet commit. Vous pouvez utiliser ce hook pour inspecter l'instantané qui est sur le point d'être commité. Par exemple, vous pourriez effectuer quelques tests automatisés pour éviter que le commit ne bogue une fonctionnalité existante.

Aucun argument n'est ajouté au script pre-commit et, si vous quittez en renvoyant un statut différent de zéro, le commit est annulé dans son intégralité. À présent, penchons-nous sur une version simplifiée (ou plus parlante) du hook pre-commit intégré. Le script annule le commit s'il trouve une erreur d'espace, comme le définit la commande git diff-index (les espaces en fin de ligne, les lignes uniquement composées d'espaces, ainsi qu'une espace suivie par une tabulation à l'intérieur du premier retrait d'une ligne sont considérées comme des erreurs par défaut).

#!/bin/sh

# Vérifiez s'il s'agit du commit initial
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    echo "pre-commit: About to create a new commit..."
    against=HEAD
else
    echo "pre-commit: About to create the first commit..."
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Utilisez git diff-index pour rechercher des erreurs d'espace
echo "pre-commit: Testing for whitespace errors..."
if ! git diff-index --check --cached $against
then
    echo "pre-commit: Aborting commit due to whitespace errors"
    exit 1
else
    echo "pre-commit: No whitespace errors :)"
    exit 0
fi

Pour utiliser git diff-index, il faut déterminer à quelle référence de commit l'index est comparé. En règle générale, il s'agit de HEAD. Cependant, HEAD n'existe pas au moment de créer le premier commit, c'est pourquoi il convient tout d'abord de traiter ce cas particulier. Nous utilisons pour cela git rev-parse --verify, qui sert à vérifier si l'argument (HEAD) est une référence valide. La partie >/dev/null 2>&1 annule toute sortie de git rev-parse. Soit HEAD, soit un objet commit vide est stocké dans la variable against en vue d'une utilisation avec git diff-index. L'empreinte 4b825d... est une référence de commit magique qui représente un commit vide.

La commande git diff-index --cached compare un commit à l'index. En ajoutant l'option --check, vous lui demandez de vous prévenir si les changements créent des erreurs d'espace. Si c'est le cas, annulez le commit en renvoyant le statut de sortie 1 ou quittez avec 0. Le workflow de commit fonctionnera de la manière habituelle.

Ce n'est qu'un exemple de hook pre-commit parmi d'autres. Il arrive que des commandes Git existantes soient utilisées pour effectuer des tests concernant les changements apportés par le commit proposé, mais vous pouvez réaliser toutes sortes d'actions dans pre-commit, comme exécuter d'autres scripts, lancer une séquence de tests tiers ou vérifier un style en utilisant Lint.

Préparer un message de commit

Le hook prepare-commit-msg est nommé une fois que le hook pre-commit a saisi un message de commit dans l'éditeur de texte. Cet emplacement est idéal pour modifier les messages de commit générés automatiquement en cas de commits écrasés ou mergés.

Jusqu'à trois des arguments suivants sont ajoutés au script prepare-commit-msg :

  1. Le nom d'un fichier temporaire qui contient le message. Changez le message de commit en modifiant le fichier en place.
  2. Le type de commit. Il peut s'agir de message (option -m ou -F), template (option -t), merge (si le commit est un commit de merge) ou squash (si le commit en écrase d'autres).
  3. L'empreinte SHA1 du commit concerné. Uniquement attribuée si l'option -c, -C ou --amend a été ajoutée.

Comme pour pre-commit, le commit est annulé si vous quittez en renvoyant un statut différent de zéro.

Nous avons examiné un exemple simple de hook permettant de modifier un message de commit. À présent, penchons-nous sur un script encore plus utile. Lorsque vous utilisez un outil de suivi de tickets, il est d'usage de traiter chaque ticket dans une branche séparée. Si vous entrez le numéro du ticket dans le nom de la branche, vous pouvez écrire un hook prepare-commit-msg pour l'inclure automatiquement dans chaque message de commit situé sur cette branche.

#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# Collectez les paramètres
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
commit_type = sys.argv[2]
else:
commit_type = ''
if len(sys.argv) > 3:
commit_hash = sys.argv[3]
else:
commit_hash = ''
print "prepare-commit-msg: Fichier : %s\nType: %s\nHash: %s" % (commit_msg_filepath, commit_type, commit_hash)
# Découvrez la branche sur laquelle nous nous trouvons
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "prepare-commit-msg: On branch '%s'" % branch
# Remplissez le message de commit avec le numéro de ticket, le cas échéant
if branch.startswith('issue-'):
print "prepare-commit-msg: Oh hey, it's an issue branch."
result = re.match('issue-(.*)', branch)
issue_number = result.group(1)
with open(commit_msg_filepath, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write("ISSUE-%s %s" % (issue_number, content))

Premièrement, le hook prepare-commit-msg ci-dessus vous indique comment récupérer tous les paramètres ajoutés au script. Ensuite, il appelle git symbolic-ref --short HEAD pour obtenir le nom de branche qui correspond à HEAD. Si le nom de la branche commence par issue-, il réécrit le contenu du fichier comprenant le message de commit pour que le numéro du ticket figure dans la première ligne. Par conséquent, si le nom de la branche est issue-224, le message de commit suivant sera créé.

ISSUE-224
# Veuillez saisir le message de commit pour vos changements. Les lignes commençant
# par '#' seront ignorées, et un message vide annule le commit.
# Sur le ticket de branche 224
# Changements à commiter :
# A modifié : test.txt

Lorsque vous utilisez prepare-commit-msg, retenez qu'il s'exécute même lorsque vous ajoutez un message avec l'option -m de git commit. Cela signifie que le script ci-dessus insèrera automatiquement la chaîne ISSUE-[#] sans que l'utilisateur ne puisse la modifier. Pour régler le problème, vérifiez que le deuxième paramètre (commit_type) est identique à message.

En revanche, sans l'option -m, le hook prepare-commit-msg permet à l'utilisateur de modifier le message après sa création, donc il s'agit plus d'un script de commodité que d'une façon d'appliquer une stratégie relative aux messages de commit. Pour ce faire, vous avez besoin du hook commit-msg, dont il est question dans la section suivante.

Message de commit

Le hook commit-msg est très similaire au hook prepare-commit-msg, mais il est appelé après que l'utilisateur a saisi un message de commit. C'est le moment de prévenir les développeurs que leur message ne correspond pas aux normes de l'équipe.

Le seul argument ajouté à ce hook est le nom du fichier qui contient le message. Si le message saisi par l'utilisateur ne convient pas, il peut modifier le fichier en place (exactement comme avec prepare-commit-msg) ou annuler l'intégralité du commit en quittant avec un statut différent de zéro.

Par exemple, le script suivant vérifie que l'utilisateur n'a pas supprimé la chaîne ISSUE-[#] créée automatiquement par le hook prepare-commit-msg dont il était question dans la section précédente.

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collectez les paramètres
commit_msg_filepath = sys.argv[1]

# Déterminez la branche sur laquelle nous nous trouvons
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "commit-msg: On branch '%s'" % branch

# Vérifiez le message de commit si nous sommes sur une branche issue
if branch.startswith('issue-'):
    print "commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)
    required_message = "ISSUE-%s" % issue_number

    with open(commit_msg_filepath, 'r') as f:
        content = f.read()
        if not content.startswith(required_message):
            print "commit-msg: ERROR! The commit message must start with '%s'" % required_message
            sys.exit(1)

Si le script est appelé à chaque fois que l'utilisateur crée un commit, évitez d'effectuer toute action, si ce n'est vérifier le message de commit. Si vous devez signaler à d'autres services qu'un instantané a été commité, optez plutôt pour le hook post-commit.

Post-commit

Le hook post-commit est appelé immédiatement après le hook commit-msg. Il ne peut modifier les conséquences de l'opération git commit, c'est pourquoi il est surtout utilisé à des fins de notification.

Le script n'accepte aucun paramètre, et son statut de sortie n'affecte le commit en aucune manière. Pour la plupart des scripts post-commit, vous souhaiterez accéder au commit qui vient d'être créé. Utilisez git rev-parse HEAD pour obtenir l'empreinte SHA1 du nouveau commit ou git log -1 HEAD pour obtenir toutes les informations le concernant.

Par exemple, si vous voulez envoyer un e-mail à votre chef à chaque fois que vous commitez un instantané (ce qui n'est certainement pas l'idée du siècle pour la plupart des workflows), vous pourriez ajouter le hook post-commit suivant.

#!/usr/bin/env python
import smtplib
from email.mime.text import MIMEText
from subprocess import check_output
# Obtenez l'entrée git log --stat du nouveau commit
log = check_output(['git', 'log', '-1', '--stat', 'HEAD'])
# Créez un e-mail en texte brut
msg = MIMEText("Look, I'm actually doing some work:\n\n%s" % log)
msg['Subject'] = 'Git post-commit hook notification'
msg['From'] = 'mary@example.com'
msg['To'] = 'boss@example.com'
# Envoyez le message
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword')
session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()

Vous pouvez utiliser post-commit pour déclencher un système d'intégration continue en local, mais la plupart du temps, vous souhaiterez effectuer cette action dans le hook post-receive. Celui-ci tourne sur le serveur plutôt que sur la machine locale de l'utilisateur et s'exécute aussi à chaque fois qu'un développeur pushe son code. Par conséquent, cet emplacement est nettement plus indiqué pour effectuer votre intégration continue.

Post-checkout

Le hook post-checkout est très similaire au hook post-commit, à cela près qu'il est appelé à chaque fois que vous parvenez à extraire une référence à l'aide de git checkout. Cette fonction est pratique pour supprimer des fichiers qui pourraient semer la confusion dans votre répertoire de travail.

Ce hook accepte trois paramètres, et son statut de sortie n'affecte en rien la commande git checkout.

  1. Réf du HEAD précédent
  2. Réf du nouveau HEAD
  3. Cette option vous indique s'il s'agissait d'un checkout de branche ou de fichier. Les options seront respectivement 1 et 0.

Les développeurs Python rencontrent souvent le même problème lorsque les fichiers .pyc générés restent actifs après le basculement d'une branche à l'autre. Il arrive que l'interpréteur utilise ces fichiers .pyc au lieu du fichier source .py. Pour éviter toute confusion, vous pouvez supprimer tous les fichiers .pyc dès que vous extrayez une nouvelle branche en utilisant le script post-checkout suivant :

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collectez les paramètres
previous_head = sys.argv[1]
new_head = sys.argv[2]
is_branch_checkout = sys.argv[3]

if is_branch_checkout == "0":
    print "post-checkout: This is a file checkout. Nothing to do."
    sys.exit(0)

print "post-checkout: Deleting all '.pyc' files in working directory"
for root, dirs, files in os.walk('.'):
    for filename in files:
        ext = os.path.splitext(filename)[1]
        if ext == '.pyc':
            os.unlink(os.path.join(root, filename))

Le répertoire de travail courant destiné aux scripts de hook est toujours placé à la racine d'un dépôt, c'est pourquoi l'appel os.walk('.') s'exécute dans chaque fichier du dépôt. Ensuite, il faut vérifier son extension et le supprimer s'il s'agit d'un fichier .pyc.

Vous pouvez également utiliser le hook post-checkout pour modifier votre répertoire de travail en fonction de la branche que vous avez extraite. Par exemple, vous pouvez utiliser une branche plugins pour stocker tous vos plug-ins hors de la base de code principale. Si ces plug-ins nécessitent beaucoup de fichiers binaires par rapport à d'autres branches, vous pourrez les créer de manière sélective une fois que vous serez sur la branche plugins.

Pre-rebase

Le hook pre-rebase est appelé avant que git rebase n'apporte le moindre changement. C'est le moment d'empêcher tout scénario catastrophe de se produire.

Ce hook nécessite deux paramètres : la branche upstream à partir de laquelle la série a été forkée et la branche rebasée. Le deuxième paramètre est vide lors du rebase de la branche courante. Pour annuler le rebase, quittez en renvoyant un statut différent de zéro.

Par exemple, si vous souhaitez rejeter tout rebase dans votre dépôt, utilisez le script pre-rebase suivant :

#!/bin/sh

# Rejetez tous les rebases
echo "pre-rebase: Rebasing is dangerous. Don't do it."
exit 1

Désormais, dès que vous exécuterez git rebase, le message suivant apparaîtra :

pre-rebase: Rebasing is dangerous. Don't do it.
The pre-rebase hook refused to rebase.

Pour obtenir un aperçu plus détaillé, examinez le script pre-rebase.sample inclus. Ce script vous sera plus utile si vous souhaitez rejeter le rebasage. Il vérifie que la branche topic que vous essayez de rebaser a déjà été mergée avec la branche next (considérée comme la branche principale). Si c'est le cas, vous allez rencontrer des difficultés en rebasant la branche, c'est pourquoi le script annule le rebasage.

Hooks côté serveur

Les hooks côté serveur fonctionnent comme les hooks locaux, sauf qu'ils sont situés dans des dépôts côté serveur (p. ex. un dépôt centralisé ou le dépôt public d'un développeur). Lorsqu'ils sont associés au dépôt officiel, certains de ces hooks permettent d'appliquer la stratégie en rejetant certains commits.

Dans le reste de cet article, nous étudierons trois hooks côté serveur :

  • Pre-receive
  • Update
  • Post-receive

Tous ces hooks correspondent aux différentes étapes du process git push.

La sortie des hooks côté serveur est redirigée vers la console du client. Il est donc très facile de renvoyer des messages au développeur. Néanmoins, vous devez également garder à l'esprit que ces scripts ne rendent pas le contrôle du terminal avant la fin de leur exécution. Vous devez donc être prudent lorsque vous effectuez des opérations de longue durée.

Pre-receive

Le hook pre-receive s'exécute à chaque fois qu'un développeur utilise git push pour pusher des commits vers le dépôt. Son emplacement devrait toujours être le dépôt distant vers lequel le push est orienté, et non le dépôt source.

Le hook s'exécute avant la mise à jour d'une référence. C'est le moment idéal pour faire appliquer une stratégie de développement. Si selon vous, le push n'est pas effectué par le bon utilisateur ou si le message de commit ou les changements contenus dans le commit sont mal formatés, il vous suffit de les rejeter. Vous ne pouvez pas empêcher un développeur de créer des commits mal formatés, mais vous pouvez éviter que ces commits n'entrent dans la base de code officielle : utilisez pre-receive pour les rejeter.

Le script ne requiert aucun paramètre, mais chaque réf pushée est transmise au script sur une ligne distincte dans l'entrée standard au format suivant :

<ancienne-valeur> <nouvelle-valeur> <nom-réf>

Vous pouvez visualiser le fonctionnement de ce hook en utilisant un script pre-receive très simple, qui lit les réfs pushées et les imprime.

#!/usr/bin/env python

import sys
import fileinput

# Lisez dans chaque réf que l'utilisateur essaie de mettre à jour
pour la ligne dans fileinput.input() :
    print "pre-receive: Trying to push ref: %s" % line

# Annulez le push
# sys.exit(1)

Encore une fois, ce hook fonctionne différemment des autres, car les informations sont transmises au script via une entrée standard, contrairement aux arguments de ligne de commande. Une fois que vous aurez placé le script ci-dessus dans le répertoire .git/hooks d'un dépôt distant et pushé la branche master, une ligne semblable à celle-ci apparaîtra dans votre console :

b6b36c697eb2d24302f89aa22d9170dfe609855b 85baa88c22b52ddd24d71f05db31f4e46d579095 refs/heads/master

Vous pouvez utiliser ces empreintes SHA1 (ainsi que certaines commandes Git de niveaux) pour examiner les changements à venir. Exemples d'utilisations :

  • Rejet des changements impliquant un rebase en amont
  • Prévention des merges sans fast-forward
  • Vérification que l'utilisateur dispose des autorisations appropriées pour apporter les changements prévus (généralement utilisée pour les workflows Git centralisés)

Si plusieurs réfs sont pushées, renvoyez un statut différent de zéro à partir de pre-receive pour annuler chacune d'entre elles. Si vous souhaitez accepter ou rejeter des branches au cas par cas, optez plutôt pour le hook update.

Update

Le hook update est appelé après pre-receive et fonctionne pratiquement de la même manière. Il est toujours appelé avant qu'un élément ne soit mis à jour, mais un appel séparé a lieu à chaque fois qu'une réf a été pushée. Cela signifie que si l'utilisateur tente de pusher quatre branches, update sera exécuté de manière distincte à quatre reprises. Contrairement à pre-receive, ce hook ne doit pas être lu à partir d'une entrée standard. En revanche, il accepte les trois arguments suivants :

  1. Le nom de la réf mise à jour
  2. Le nom de l'ancien objet stocké dans la réf
  3. Le nom du nouvel objet stocké dans la réf

Les mêmes informations sont transmises à pre-receive, mais puisque update est appelé de manière distincte pour chaque réf, vous pouvez rejeter certaines réfs et en accepter d'autres.

#!/usr/bin/env python
import sys
branch = sys.argv[1]
old_commit = sys.argv[2]
new_commit = sys.argv[3]
print "Moving '%s' from %s to %s" % (branch, old_commit, new_commit)
# Annulez en pushant uniquement cette branche
# sys.exit(1)

Le hook update ci-dessus génère simplement la branche et les anciennes/nouvelles empreintes de commit. Si vous pushez plusieurs branches vers le dépôt distant, vous constatez que l'instruction print s'exécute pour chaque branche.

Post-receive

Le hook post-receive est appelé après un push réussi. C'est un emplacement idéal pour effectuer des notifications. Bon nombre de workflows préfèrent cet emplacement à post-commit, car les changements sont accessibles sur un serveur public et n'apparaissent pas uniquement sur l'ordinateur local de l'utilisateur. Le hook post-receive est souvent utilisé pour envoyer des e-mails à d'autres développeurs ou pour enclencher un système d'intégration continue.

Le script n'accepte aucun paramètre, mais reçoit les mêmes informations que pre-receive via l'entrée standard.

Summary

Dans cet article, nous avons appris comment les hooks Git peuvent être utilisés pour modifier les comportements en interne et recevoir des notifications lorsque des événements spécifiques se produisent dans un dépôt. Les hooks sont des scripts ordinaires qui résident dans le dépôt .git/hooks, ce qui facilite leur installation et leur personnalisation.

Par ailleurs, nous avons examiné certains des hooks locaux et côté serveur les plus courants. À présent, voyons le cycle de vie complet du développement. Nous savons comment exécuter des actions personnalisées à chaque étape du processus de création des commits et du processus git push. Une fois que vous aurez acquis quelques connaissances sur les scripts, les dépôts Git n'auront plus de secret pour vous.