Quel que soit le produit développé ou le langage utilisé, il y a toujours des développeurs et des commits quelque part…
Au travers de l’historique, les commits permettent de retracer toutes les modifications effectuées sur un projet.
Néanmoins, la qualité des messages peut beaucoup varier d’un développeur à l’autre, voire d’un jour à l’autre pour la même personne…
Quand nous regardons l’historique d’un projet versionné sous git, nous constatons parfois une suite de commits peu expressifs…
e60930a9 (HEAD -> my_branch, origin/mybranch) Modif de la nav
49ff1d07 Fix describe bis
49ff1d07 Fix describe
3672f204 Update conf
03d00e2e Types
a5fdbc4b #261 suppress unecessary lines
886fd7af add logo and modify message for loading connections
a06a1272 support logo and redirection
1dd84e9c fix reconnect insert #291
a62f7880 No comment
26f99ee0 ...
d2be491c fix test
bfb2f370 commit final with all fix
Les développeurs arrivant sur le projet, ou pire ceux qui sont déjà sur le projet, peuvent difficilement exploiter un tel historique.
Pour citer quelques problématiques liées à un tel historique :
Les développeurs ont tendance à écrire des messages qui ne sont pas assez expressifs. Ils ne décrivent pas toujours les changements opérés ni l’objectif du code ajouté.
Chaque message de commit devrait pouvoir répondre aux questions suivantes :
Quels types de changement suis-je en train de faire ?
Quel périmètre est affecté par mes changements ?
Quelles sont les modifications opérées ?
Git ne définit pas de format de message. Cela reste du texte simple où chacun est libre de structurer son message comme il le souhaite.
La spécification “conventional Commit” propose d’ajouter une couche de vérification des messages avant de les écrire dans Git.
L’objectif est de créer une convention commune au sein du projet et faciliter ainsi le partage du code ainsi que les résolutions de bugs.
Cette spécification est largement inspirée des bonnes pratiques de contribution du projet Angular
Les messages sont composés comme suit :
<type>[optional scope]: <subject>
<BLANK LINE>
[optional body]
<BLANK LINE>
[optional footer]
Type
décrit ce que fait votre commit.
Voici les valeurs couramment utilisées :
Scope
Décrit le périmètre de la modification (ex: nom du composant, module, packages, etc)
Subject
Description succincte des modifications
Body (optionnel)
Décrit plus en détail les objectifs et intentions qui ont motivé les changements apportés
Footer (optionnel)
Liens éventuels vers des outils de suivi de bugs
A noter :
En cas de “Breaking change”, il est possible de l’indiquer via un ‘!’ dans le type et dans le body en ajoutant “BREAKING CHANGE:” suivi de l’explication.
Fonctionnalité implémentée : Possiblité de passer commande via un bouton
feat(order): add purchase order button
Mise à jour de la documentation pour expliquer les scripts d’initialisation du projet
docs(readme): document how to start project
Arrêt du support Node 6
refactor!: drop support for Node 6
BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.
Pour vérifier que tout le monde respecte bien le format défini par le projet, il convient de faire valider les messages.
La librairie “Commit Lint” est justement là pour nous aider !
npm install -g @commitlint/cli @commitlint/config-conventional
Rq: il est également possible d’installer localement ou via npx la librairie.
Création du fichier de config commitlint.config.js
lue par la librairie
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
Les types autorisés par cette configuration sont : ‘build’, ‘ci’, ‘chore’, ‘docs’, ‘feat’, ‘fix’, ‘perf’, ‘refactor’, ‘revert’, ‘style’, ‘test’
Consultez la documentation pour les autres règles prédéfinies
# Lint from stdin
echo 'foo: bar' | commitlint
⧗ input: foo: bar
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert,
style,test] [type-enum]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
Il est par exemple possible de forcer l’usage des scopes et d’en restreindre le choix à une liste prédéfinie
//commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-empty': [2, 'never'],
'scope-enum': [2, 'always', [
'logging',
'services',
'docs',
'dependencies',
'profiles',
'users',
'api',
'segments',
'configuration'
]]
}
};
Pour aller plus loin, consultez la liste des règles de commitlint
86f80528 (HEAD -> master, origin/master, origin/HEAD) fix(changelog): display scope in breaking change messages
6b578392 fix(CLI): show symlinked themes in `omz theme list`
64cb1530 chore: add Konfekt as universalarchive maintainer
492f712d feat(plugins): add `universalarchive` plugin to conveniently compress files (#6846)
2118d35e fix(vi-mode)!: add back edit-command-line key binding as 'vv' (#9573)
79980b00 fix(vi-mode): hide cursor-change logic behind `VI_MODE_SET_CURSOR` setting
94ce46d4 docs(vi-mode): revamp README and document settings
66e0438d fix(archlinux): update URL and key server in `pacmanallkeys` (#9569)
9e5f280f feat(CLI): add `plugin info` subcommand (#9452)
Logs extraits du projet OhMyzsh
Pour garantir la structure des messages de commit et ne pas ralentir le développeur, l’idéal est de vérifier automatiquement le format à la création du commit.
Git permet justement d’exécuter des scripts personnalisés au déclenchement de certaines actions (documentation sur les Git Hooks)
Le hook qui nous intéresse est “commit-msg”
Créer un fichier .git/hooks/commit-msg
#!/bin/sh
commitlint > $1
status=$?
if [ $status -gt 0 ]
then
echo "✖ Commit message does not follow conventional commit message spec"
echo 'ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
fi
exit $status
Husky est une dépendance npm
qui nous aide à facilement configurer les “Git Hooks”
npm install --save-dev husky
Configuration du hook ‘commit-msg’ pour exécuter le linter
// package.json
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
$ git commit -m “foo”
husky > commit-msg (node v12.19.0)
⧗ input: foo
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
husky > commit-msg hook failed (add --no-verify to bypass)
Appliquer cette rigueur peut être perturbant au début mais cela devient très vite un réel atout.
L’historique des commits n’est plus une liste inexploitable que personne ne consulte. Il devient une véritable aide à la compréhension des modifications opérées dans le projet.
En soignant ses messages de commit, le développeur se pose également plus de questions sur ce qu’il est en train de faire et si les changements apportés dans le commit sont bien adaptés (voir les bonnes pratiques des commits atomiques)
En cas de problème d’historique, ne pas oublier le “rebase interactif” :-)
Avoir des messages expressifs et structurés permet également d’automatiser certaines tâches.
Dans un prochain article nous verrons comment automatiser la création d’un changelog lors de la livraison d’une nouvelle version.
Photo de couverture par Yancy Min sur Unsplash