Aller au contenu principal
Nicolas Cousin Tech SolutionsNicolas Cousin Tech Solutions
Module 6 sur 6

Hooks natifs vs hooks gérés par pre-commit

Ce que vous allez faire

Ce module est court et dépend directement du précédent : vous avez besoin du dépôt mon-projet-pre-commit créé au module 5, avec pre-commit install déjà exécuté dedans. Vous allez ouvrir le script que cette commande a généré, comprendre qu'il ne vient pas de vous, puis le comparer brièvement à un hook git natif écrit à la main.

Ouvrir le hook généré

Dans le dossier mon-projet-pre-commit, ouvrez le fichier généré à l'étape 4 du module précédent :

cat .git/hooks/pre-commit

Vous devriez voir un script proche de celui-ci (le contenu exact peut légèrement varier selon la version de pre-commit installée) :

#!/usr/bin/env bash
# File generated by pre-commit: https://pre-commit.com
# ID: 138fd403232d2ddd5efb44317e38bf03

# start templated
INSTALL_PYTHON=/path/to/bin/python
ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit)
# end templated

HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")

if [ -x "$INSTALL_PYTHON" ]; then
  exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
elif command -v pre-commit > /dev/null; then
  exec pre-commit "${ARGS[@]}"
else
  echo '`pre-commit` not found. Did you forget to activate your virtualenv?' 1>&2
  exit 1
fi

Le commentaire en tête de fichier est explicite : File generated by pre-commit. Vous n'avez écrit aucune ligne de ce script. Ce qu'il fait, concrètement : il repère un interpréteur Python (ou, à défaut, l'exécutable pre-commit disponible dans votre PATH), puis lui délègue tout le travail réel en lui passant hook-impl et le chemin vers .pre-commit-config.yaml. C'est cet appel délégué qui va ensuite lire votre configuration, résoudre le hook trailing-whitespace, et l'exécuter — exactement ce que vous avez observé au module 5.

Autrement dit : .git/hooks/pre-commit n'est pas le hook qui vérifie les espaces en fin de ligne. C'est un simple dispatcher — un aiguillage — qui transmet la main à pre-commit, lequel lit à son tour votre configuration versionnée pour savoir quoi exécuter réellement.

Ce que dit la documentation officielle de Git sur les hooks natifs

D'après la documentation officielle de Git, le hook pre-commit est un script que git invoque automatiquement juste avant de finaliser un commit ; si ce script se termine avec un code de sortie différent de zéro, git annule le commit. C'est exactement le mécanisme que vous avez déclenché au module 5 — sauf qu'à la place d'un script écrit à la main, c'est le dispatcher généré par pre-commit qui occupe cet emplacement.

Git cherche ce script dans le dossier .git/hooks du dépôt par défaut. Ce dossier n'est pas versionné : il n'est jamais poussé ni cloné avec le reste du dépôt, ce qui explique pourquoi chaque personne doit relancer pre-commit install après avoir cloné un projet qui utilise pre-commit — sans ça, le fichier .git/hooks/pre-commit n'existe simplement pas chez elle.

Git propose aussi la variable de configuration core.hooksPath, qui permet de pointer vers un dossier de hooks différent — potentiellement partagé et versionné, en dehors de .git/hooks. C'est une façon alternative de gérer des hooks natifs à l'échelle d'une équipe, mais ce n'est pas le mécanisme utilisé par pre-commit : pre-commit install écrit dans le dossier de hooks par défaut du dépôt (.git/hooks). Si core.hooksPath pointe déjà ailleurs, pre-commit ne devine pas et n'écrit pas silencieusement à cet autre endroit — il refuse l'installation avec une erreur explicite (Cowardly refusing to install hooks with 'core.hooksPath' set.). Un cas rare en première installation, mais utile à savoir si vous croisez un jour ce message.

Hook natif écrit à la main, pour contraste

Si vous n'aviez pas utilisé pre-commit, vous auriez pu écrire vous-même un script exécutable directement dans .git/hooks/pre-commit, par exemple un script shell de quelques lignes qui grep le contenu stagé à la recherche du mot TODO et refuse le commit s'il en trouve un. Ça fonctionnerait, au sens où git l'invoquerait exactement de la même façon. Mais ce hook resterait propre à votre machine, non versionné, non partagé avec l'équipe, et sans aucun mécanisme de mise à jour — chaque amélioration devrait être recopiée manuellement chez chaque coéquipier.

C'est précisément ce que pre-commit ajoute par-dessus le mécanisme natif de git : une configuration versionnée (.pre-commit-config.yaml, commitée avec le code), des hooks réutilisables et maintenus par d'autres (comme trailing-whitespace), et une commande unique (pre-commit install) pour que chaque personne qui clone le projet active le même comportement.

Ce que vous venez de faire

Vous avez ouvert le script généré par pre-commit install et vérifié, texte à l'appui, qu'il ne fait rien d'autre que déléguer à pre-commit — ce n'est ni un hook que vous avez écrit, ni de la magie : c'est un hook git natif tout à fait ordinaire du point de vue de git, qui se trouve pointer vers un outil externe plutôt que contenir la logique de vérification directement. Vous savez maintenant situer les deux couches : le mécanisme de hooks de git, natif et documenté depuis toujours, et pre-commit, qui s'appuie dessus pour rendre les vérifications versionnables et partageables entre coéquipiers.

Ce module clôt le parcours : vous êtes passé d'aucune exposition aux Dev Containers et à pre-commit à une compréhension pratique des deux — capable d'ouvrir et de lire un Dev Container, et d'installer, configurer, et déclencher pre-commit en connaissance de cause.

Vérifiez votre compréhension

Le fichier .git/hooks/pre-commit généré au module précédent — qu'est-ce que c'est exactement ?

Selon la documentation officielle de Git, qu'est-ce qui détermine où git va chercher le hook pre-commit à exécuter avant un commit ?

Un hook git natif écrit entièrement à la main (sans pre-commit) et le hook généré par pre-commit install ont-ils le même point d'entrée pour git ?

Envie d'être prévenu des prochains modules ?

L'Académie reste gratuite et en accès libre, sans inscription. Si vous voulez juste être averti par email à la sortie d'un nouveau module, c'est ici — aucune obligation, désinscription en un clic.