infra / git-github
Hooks Git
Explication
Ce que vous allez apprendre
- Comprendre le rôle des hooks
pre-commitetcommit-msgdans le cycle de vie d'un commit - Écrire un hook simple qui bloque un commit si le linter échoue
- Réaliser pourquoi
.git/hooks/n'est jamais partagé automatiquement avec l'équipe - Rendre les hooks versionnés et partagés avec Husky et lint-staged
- Utiliser
--no-verifyavec parcimonie, sans en faire une habitude
Dans quel contexte ?
Sur le dépôt boutique-api, un développeur committe régulièrement du code avec des erreurs ESLint, découvertes seulement plus tard en intégration continue, ce qui ralentit toute l'équipe. Un hook pre-commit, configuré avec Husky pour être partagé automatiquement par toute l'équipe dès npm install, bloque désormais ce type de commit directement sur la machine du développeur, avant même qu'il n'atteigne GitHub.
Le problème d'une règle qui repose sur la mémoire
D'abord, dire à toute une équipe "pensez bien à lancer le linter avant de committer" fonctionne rarement dans la durée : quelqu'un finit toujours par l'oublier, surtout sous pression.
La solution : automatiser plutôt que demander
Les hooks Git déplacent cette responsabilité de la mémoire humaine vers l'automatisation : ce sont des scripts que Git exécute lui-même, automatiquement, à des moments précis du cycle de vie d'un commit ou d'un push.
Un premier moment d'intervention
Le hook pre-commit s'exécute juste avant que le commit soit finalisé : c'est le bon endroit pour vérifier la qualité du code, comme un linter ou un formatage, et bloquer l'opération si quelque chose ne va pas.
Un deuxième moment, sur le message lui-même
Le hook commit-msg intervient un peu plus tard, sur le message du commit : il peut imposer un format précis, par exemple la convention "type(scope): description".
Ce que ce format apporte à l'équipe
Cela garantit un historique de messages cohérent dans tout le projet, ce qui facilite ensuite la génération automatique de changelogs.
| Hook | Moment d'exécution | Usage typique |
|---|---|---|
pre-commit | Juste avant la finalisation du commit | Linter, formatage automatique |
commit-msg | Sur le message du commit | Imposer un format (type(scope): description) |
pre-push | Avant l'envoi vers le dépôt distant | Lancer la suite de tests complète |
Un piège que beaucoup découvrent trop tard
Les hooks classiques vivent dans un dossier local, .git/hooks/, qui n'est JAMAIS inclus dans les commits ni partagé via GitHub.
Ce que ça signifie pour l'équipe
Chaque personne qui clone le dépôt doit configurer ses propres hooks manuellement, ce qui rend la règle facilement contournable par oubli.
La solution moderne à ce piège
Des outils comme Husky résolvent précisément ce problème en rendant la configuration des hooks elle-même versionnée et installée automatiquement quand quelqu'un installe les dépendances du projet.
Un contournement à utiliser avec parcimonie
Enfin, l'option qui permet d'ignorer un hook existe pour de bonnes raisons, une urgence réelle, un cas exceptionnel, mais elle ne doit jamais devenir une habitude : un hook contourné systématiquement n'a plus aucune valeur protectrice.
Piège fréquent
Utiliser git commit --no-verify régulièrement pour "gagner du temps" revient à désactiver silencieusement toutes les protections mises en place par l'équipe (linter, format de message). Réserve cette option aux urgences documentées, jamais à un usage systématique.
La leçon suivante prend du recul sur les outils individuels vus jusqu'ici pour aborder une question d'équipe : comment s'organiser collectivement autour de Git, avec les workflows d'équipe.
Commandes & code
Hooks Git
Les hooks exécutent un script automatiquement à certains moments du cycle de vie Git (commit, push...).
ls .git/hooks/
# pre-commit.sample, pre-push.sample, commit-msg.sample... (désactivés par défaut, suffixe .sample)#!/bin/sh
# .git/hooks/pre-commit (rendre exécutable : chmod +x .git/hooks/pre-commit)
# Bloque le commit si le linter échoue
npx eslint . --max-warnings=0
if [ $? -ne 0 ]; then
echo "Commit annulé : erreurs ESLint détectées."
exit 1
fi#!/bin/sh
# .git/hooks/commit-msg - valide le FORMAT du message de commit
# reçoit en $1 le chemin du fichier contenant le message
commit_msg=$(cat "$1")
pattern="^(feat|fix|chore|docs|refactor|test)(\(.+\))?: .{1,72}$"
if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "Message de commit invalide."
echo "Format attendu : type(scope): description (ex: feat(auth): add password reset)"
exit 1
fi# Problème : .git/hooks/ n'est PAS versionné, donc PAS partagé avec l'équipe par défaut
# Solution moderne : Husky (Node.js) installe des hooks versionnés dans le dépôt
npm install --save-dev husky
npx husky init
echo "npx lint-staged" > .husky/pre-commit
git add .husky/pre-commit
git commit -m "chore: add pre-commit hook via husky"// package.json - lint-staged limite le linting aux fichiers STAGÉS, pour rester rapide
{
"lint-staged": {
"*.js": ["eslint --fix", "prettier --write"],
"*.md": ["prettier --write"]
}
}# Contourner un hook ponctuellement (à utiliser avec parcimonie, jamais en réflexe)
git commit --no-verify -m "hotfix: bypass lint for emergency deploy"Résumé
- Les hooks locaux (
.git/hooks/) ne sont pas versionnés : Husky les rend partageables via npm. pre-commitvalide le code avant de committer,commit-msgvalide le FORMAT du message.lint-stagedlimite les vérifications aux fichiers réellement modifiés, pour rester rapide.--no-verifycontourne les hooks : à réserver aux cas exceptionnels documentés.
Exercices pratiques
Mission : rendre un garde-fou vraiment partagé par l'équipe
Objectif : Diagnostiquer pourquoi un hook local ne protège pas toute l'équipe, et le rendre versionné avec Husky.
Contexte
Un développeur configure un hook pre-commit manuellement dans .git/hooks/ sur sa machine pour bloquer les commits contenant des erreurs ESLint. Deux semaines plus tard, un collègue qui a cloné le même dépôt commite du code plein d'erreurs de lint sans jamais être bloqué.