Connaître les commandes Git, c'est une chose. Travailler à cinq sur le même dépôt sans se marcher dessus, c'en est une autre. Le jour où tout le monde pousse directement sur main, on finit toujours par avoir la même conversation : « C'est quelle version qui tourne en prod, là ? » Et personne ne sait vraiment répondre.
Git Flow est la réponse la plus connue à ce problème. C'est un modèle d'organisation des branches, proposé par Vincent Driessen en 2010, qui a été adopté (et parfois sur-adopté) dans énormément d'équipes. Je vous l'explique ici de débutant à expert, avec la vidéo du cours, puis je vous dis franchement dans quels cas je ne l'utiliserais pas.
Le problème qu'on essaie de résoudre
Si toute l'équipe travaille directement sur main, la branche se remplit de fonctionnalités à moitié terminées. Le jour où un bug apparaît en production, impossible de savoir quel commit correspond à une version stable. Et impossible de livrer une correction rapide sans embarquer au passage tout le travail en cours des collègues.
Ce qu'il faut, c'est séparer clairement trois choses : ce qui est en production, ce qui est en cours d'intégration, et ce qui est en cours de développement.
Deux branches permanentes
mainne contient que des versions en production. Chaque commit surmainest une version officielle, taguée.developest la branche d'intégration. C'est là qu'on rassemble les nouveautés pour la prochaine version.
Ces deux branches vivent pour toujours. Toutes les autres sont temporaires et disparaissent une fois leur travail terminé.
Les branches temporaires
feature : une branche par fonctionnalité
Chaque nouvelle fonctionnalité part de develop et y revient.
git switch develop
git switch -c feature/panier-sauvegarde
# ... travail, commits ...
git switch develop
git merge --no-ff feature/panier-sauvegarde
git branch -d feature/panier-sauvegarde
Chacun travaille tranquillement dans son coin. develop grossit petit à petit, fonctionnalité par fonctionnalité.
release : préparer une version
Quand develop contient assez de nouveautés, on « gèle » une version en créant une branche release.
git switch -c release/1.1.0 develop
À partir de ce moment, plus aucune nouvelle fonctionnalité n'entre dans cette branche. On corrige seulement les derniers bugs, on met à jour le numéro de version, le changelog. Pendant ce temps, le reste de l'équipe peut continuer à fusionner des features dans develop pour la version d'après. C'est tout l'intérêt.
Une fois prête, la release part à deux endroits :
git switch main
git merge --no-ff release/1.1.0
git tag -a v1.1.0 -m "Version 1.1.0"
git switch develop
git merge --no-ff release/1.1.0 # pour récupérer les corrections de dernière minute
git branch -d release/1.1.0
Le retour dans develop est l'étape qu'on oublie le plus souvent. Et c'est comme ça qu'un bug corrigé en release réapparaît mystérieusement dans la version suivante.
hotfix : réparer la production en urgence
Un bug grave en production ? On ne passe pas par develop, qui contient du travail pas encore prêt. On part directement de main :
git switch -c hotfix/1.1.1 main
# correction + commit
git switch main
git merge --no-ff hotfix/1.1.1
git tag -a v1.1.1 -m "Correctif paiement"
git switch develop
git merge --no-ff hotfix/1.1.1
git branch -d hotfix/1.1.1
Même règle que pour la release : le correctif doit aussi revenir dans develop. Sinon la prochaine version réintroduit le bug.
Le schéma complet
feature/* ──part de──▶ develop ──revient dans──▶ develop
release/* ──part de──▶ develop ──revient dans──▶ main (+ tag) ET develop
hotfix/* ──part de──▶ main ──revient dans──▶ main (+ tag) ET develop
main: la productiondevelop: l'intégrationfeature/*: le développementrelease/*: la préparation d'une versionhotfix/*: la réparation urgente
L'extension git flow
Taper toutes ces commandes à la main devient vite fastidieux. L'extension git-flow (la version la plus répandue est git-flow AVH Edition, disponible dans la plupart des gestionnaires de paquets et livrée avec Git pour Windows) les regroupe :
git flow init # crée develop et définit les préfixes
git flow feature start panier # = switch -c feature/panier depuis develop
git flow feature finish panier # = merge --no-ff dans develop + suppression
git flow release start 1.1.0
git flow release finish 1.1.0 # merge dans main, tag, merge dans develop
git flow hotfix start 1.1.1
git flow hotfix finish 1.1.1
C'est confortable, mais je conseille toujours de faire les manipulations à la main au moins une fois. Le jour où l'extension n'est pas installée sur une machine, ou qu'un finish s'arrête sur un conflit, vous saurez exactement où vous en êtes.
Niveau expert : pourquoi --no-ff ?
Par défaut, si la branche cible n'a pas bougé, Git fait un fast-forward : il avance simplement le pointeur, et vos commits de feature se retrouvent alignés dans l'historique de develop, sans trace de la branche.
L'option --no-ff force la création d'un commit de fusion, même quand ce n'est pas nécessaire. Résultat : dans git log --graph, chaque fonctionnalité apparaît comme un bloc identifiable. Et si une fonctionnalité pose problème, vous pouvez annuler le bloc entier en une seule commande :
git revert -m 1 <hash-du-commit-de-merge>
Les numéros de version : majeur.mineur.correctif
Git Flow va très bien avec le versionnage sémantique (SemVer) :
- Correctif (1.0.0 → 1.0.1) : un hotfix, rien ne change pour l'utilisateur à part le bug corrigé.
- Mineur (1.0.1 → 1.1.0) : une release avec de nouvelles fonctionnalités, compatible avec l'existant.
- Majeur (1.1.0 → 2.0.0) : un changement qui casse la compatibilité.
Le numéro devient une information, pas juste un compteur. Quand vous voyez passer une 2.0.0, vous savez qu'il faut lire les notes de version avant de mettre à jour.
En équipe : pull requests et branches protégées
En pratique, on ne fusionne presque jamais à la main dans develop ou main. On ouvre une pull request (ou merge request chez GitLab) :
- un collègue relit le code ;
- la CI lance les tests automatiquement ;
- la fusion n'est possible que si les deux sont au vert.
Et on protège main et develop dans les paramètres du dépôt, pour que personne ne puisse y pousser directement. C'est le genre de règle qui paraît bureaucratique jusqu'au jour où elle vous évite un push --force malheureux sur la branche de production.
Quand ne PAS utiliser Git Flow
C'est la partie que les tutoriels oublient souvent, et Vincent Driessen lui-même l'a ajoutée en note sur son article d'origine : Git Flow est fait pour des logiciels avec des versions planifiées. Une application mobile qui sort tous les mois, un logiciel installé chez des clients, une bibliothèque avec plusieurs versions maintenues en parallèle.
Si vous déployez plusieurs fois par jour (un site web, une API, un SaaS), Git Flow devient un frein : des branches qui vivent trop longtemps, des fusions douloureuses, une release qui n'apporte rien puisque tout part en prod en continu. Dans ce cas, deux alternatives :
- GitHub Flow : une seule branche
maintoujours déployable, et de courtes branches de travail fusionnées via pull request. - Trunk-based development : tout le monde intègre sur
mainau moins une fois par jour, avec des feature flags pour cacher ce qui n'est pas prêt.
Pour mes propres projets web, j'utilise un GitHub Flow simple. Je garde Git Flow pour les projets où une notion de « version » a vraiment un sens pour les utilisateurs.
À retenir
mainpour la production,developpour l'intégration.featurepour développer,releasepour préparer une version,hotfixpour réparer en urgence.- Release et hotfix repartent toujours dans
develop. --no-ffpour garder chaque fonctionnalité visible dans l'historique.- Git Flow seulement si vos versions sont planifiées. Sinon, faites simple.
Si les commandes de base vous paraissent encore floues, commencez par mon article sur Git de zéro à avancé. Et pour automatiser les tests sur chaque merge request, la suite logique est GitLab CI/CD.