107 commits. Des conventional commits impeccables depuis le premier jour. Feat, fix, refactor, chore — tout parfaitement étiquetté. Et le CHANGELOG ? Vide. Inexistant. Un fichier que “j’écrirai demain” pendant deux mois.
Si ça vous dit quelque chose, vous n’êtes pas seuls. Écrire un changelog à la main, c’est un cauchemar de catégorie olympique. Ce n’est pas que ce soit difficile — c’est que c’est pénible, répétitif, et il y a toujours quelque chose de plus urgent à faire. Et c’est justement pour ça que git-cliff existe.
Qu’est-ce que git-cliff (en 30 secondes)
C’est un générateur de changelogs écrit en Rust qui lit vos commits git, les parse selon les conventional commits, et produit un CHANGELOG.md groupé par version et type. Sans dépendances bizarres, sans plugins, sans magie noire. Un binaire, un fichier de configuration, et c’est parti.
En bon français : vous lui donnez vos commits et il vous rend le fichier que vous reportez depuis des mois.
| |
Ces deux lignes sont littéralement tout ce qu’il faut pour commencer. Si vos commits suivent la convention type: description, git-cliff les comprend sans configuration supplémentaire.
Le cas réel : changelog rétroactif pour 8 versions
Dans Tokamak (mon app de menu bar pour surveiller le quota Claude) j’avais exactement ce problème : 107 commits parfaits et un CHANGELOG vide. L’app était déjà en v1.3.0 mais il n’existait qu’un tag v0.1 du début des temps.
Le plan était simple :
Étape 1 : Créer des tags rétroactifs sur les commits de chaque version.
| |
Étape 2 : Exécuter git-cliff.
| |
Résultat : un CHANGELOG.md complet avec 8 versions, chacune avec ses features, bug fixes, refactors et chores groupés. 189 lignes. 30 secondes.
La configuration : cliff.toml
Git-cliff utilise un fichier TOML avec deux sections principales : [changelog] pour le format de sortie et [git] pour comment interpréter les commits.
Voici la configuration que j’utilise :
| |
Trois choses à noter :
trim_start_matches(pat="v")— Les tags sontv1.3.0mais dans le changelog je veux1.3.0. Ce filtre Tera le fait.filter_unconventional = true— Écarte les commits qui ne suivent pas la convention. Si vous avez un repo avec des anciens commits du genre “fixed stuff”, mettez-le àfalseet ajoutez un parser catch-all :{ message = ".*", group = "Other" }.sort_commits = "oldest"— Les commits dans chaque groupe vont dans l’ordre chronologique. Pour l’ordre inverse :"newest".
Templates : le langage dont vous ne saviez pas avoir besoin
Le body utilise Tera, un moteur de templates inspiré de Jinja2. La courbe d’apprentissage n’est pas nulle, mais ce n’est pas Haskell non plus. Quelques filtres utiles :
| Filtre | Ce qu’il fait | Exemple |
|---|---|---|
trim_start_matches | Enlève le préfixe | v1.0 → 1.0 |
upper_first | Met en capitale | features → Features |
split + first | Première ligne | Ignore le corps du commit |
date | Formate la date | %Y-%m-%d |
group_by | Groupe | Par type de commit |
L’astuce la plus utile : commit.message | split(pat="\n") | first extrait seulement la première ligne du message de commit. Si vous écrivez des corps longs dans vos commits (et vous devriez), cela évite que le changelog devienne un roman.
Gotcha : le whitespace de Tera
C’est celui qui m’a pris le plus de temps. Les templates Tera sont sensibles aux whitespaces. Un saut de ligne en trop ou en moins dans le template et votre changelog sort avec des trous bizarres ou des sections collées.
Les clés :
{%-(avec tiret) : supprime le whitespace avant le tag-%}(avec tiret) : supprime le whitespace après le tagtrim = truedans[changelog]: supprime le whitespace de chaque ligne générée
Le problème c’est que trim = true mange aussi les sauts de ligne que vous voulez garder — comme la séparation entre versions. La solution est de laisser un {% endfor %} sans tiret à la fin de la boucle extérieure, pour que Tera émette le saut supplémentaire.
Ce n’est pas exactement intuitif. Mais une fois que ça marche, ça marche.
Cas d’usage qui ne sont pas évidents
Git-cliff n’est pas juste “génère un CHANGELOG et c’est tout”. Il y a des usages qui valent la peine d’être connus :
Générer seulement la dernière version
| |
Parfait pour les release notes. Au lieu du changelog complet, ça ne vous donne que la section de la version la plus récente. Idéal pour le coller dans la description d’une release GitHub ou Gitea.
Preview sans tag
| |
Génère la section d’une version que vous n’avez pas encore taguée. Utile pour réviser ce que va inclure votre prochaine release avant de la créer.
Bump automatique
| |
Git-cliff analyse les commits depuis le dernier tag et vous dit quelle version devrait être la suivante selon semver : s’il n’y a que des fix, il monte le patch ; s’il y a des feat, il monte le minor ; s’il y a des breaking changes, il monte le major.
Changelog par plage
| |
Seulement les commits entre deux tags. Utile pour générer les notes d’un hotfix ou pour auditer ce qui a changé entre deux versions spécifiques.
Monorepos
Si votre repo a plusieurs projets, git-cliff supporte --include-path pour filtrer les commits qui affectent un chemin concret :
| |
Chaque sous-projet peut avoir son propre cliff.toml et son propre changelog.
L’intégrer dans votre flux (sans l’oublier)
Le plus gros risque de git-cliff n’est pas technique — il est humain. Vous allez l’oublier. Je le sais parce que c’est arrivé.
La solution la plus pragmatique : l’intégrer comme dépendance de l’étape dont vous allez vous souvenir de faire. Dans mon cas, le Makefile :
| |
Maintenant make archive régénère le changelog automatiquement avant de compiler pour la release. Je n’ai pas besoin de me souvenir de quoi que ce soit — quand je vais publier, le changelog se met à jour tout seul.
Autres options raisonnables :
- GitHub Actions :
git cliff --latestdans le workflow de release pour générer les notes automatiquement. - Pre-push hook : régénérer avant chaque push. Plus agressif, plus bruyant.
- Post-tag hook : régénérer lors de la création d’un tag. Le plus logique sémantiquement, mais git n’a pas de hook natif pour les tags (il faudrait un wrapper).
Ma recommandation : attachez le changelog à l’étape de release. C’est là que ça compte vraiment. Un changelog pas à jour sur main entre les releases, tout le monde s’en fiche.
Conventional commits : le prérequis
Tout cela suppose que vos commits suivent la convention type(scope): description :
feat: add login with OAuth
fix: prevent crash on empty response
refactor: extract auth logic to separate module
chore: update dependencies
docs: add API reference
test: add edge cases for parser
Si vos commits sont du genre “wip”, “stuff”, “asdf” et “fix things maybe”, git-cliff ne va pas faire de miracles. Ordures qui entrent, ordures qui sortent.
Mais la bonne nouvelle c’est qu’il ne faut pas d’outils pour faire des conventional commits. C’est juste une convention de nommage. Ceci dit, si vous voulez de la validation, commitlint ou un pre-commit hook simple vous assure que personne (vous y compris à 3h du matin) ne rate le format.
Pourquoi git-cliff et pas autre chose
Il y a des alternatives : standard-version, semantic-release, conventional-changelog, release-please. Elles marchent toutes. Mais git-cliff a trois avantages qui pour moi sont décisifs :
C’est un binaire.
brew installet ça marche. Sans Node, sans Python, sans écosystème. Dans un projet Swift comme le mien, je ne veux pas ajouterpackage.jsonjuste pour le changelog.C’est rapide. 120 millisecondes pour 10.000 commits (selon des benchmarks réels). Les alternatives basées sur Node prennent 30 secondes pour le même repo.
Le template est à vous. Si vous voulez des emojis, vous les mettez. Si vous voulez des liens vers les issues, vous les mettez. Si vous voulez le changelog en YAML, vous pouvez. Le moteur Tera vous donne un contrôle total sur l’output.
Le résultat
De 0 à changelog complet en moins de 5 minutes :
## 1.3.0 — 2026-02-22
### Features
- QuotaFetchStrategy pipeline with fallback for resilient quota fetching
- semi-automatic screenshot capture for App Store (8 locales × 3 scenarios)
### Bug Fixes
- attack phrase wrapping in exhausted view (TOK-115)
- resolve all notarization signing issues (TOK-93/94/95/96)
### Refactor
- QuotaProvider protocol for multi-provider support (TOK-53)
## 1.2.0 — 2026-02-22
...
8 versions, 107 commits, groupés par type, avec dates et références aux issues. Sans écrire une seule ligne à la main.
La prochaine fois que quelqu’un vous demande “qu’est-ce qui a changé dans la dernière version ?”, vous n’aurez pas à fouiller dans les commits ni improviser de mémoire. Vous aurez un fichier. Généré automatiquement. Qui se met à jour tout seul quand vous publiez.
Et si un jour vous changez d’avis sur le format, vous changez le template. Les commits — vos commits habituels — sont la source de vérité. git-cliff ne fait que les présenter joliment.