Le schéma de Git Flow, avec ses branches develop, release et hotfix qui s'entrecroisent, circule depuis plus de quinze ans, des supports de cours jusqu'aux questions d'entretien. Beaucoup d'équipes l'ont adopté parce qu'il était là, sans se demander s'il collait à leur façon de livrer.
Pour une personne en reconversion ou un développeur junior qui arrive sur son premier projet à plusieurs, la question tombe vite : pourquoi cette équipe travaille avec une branche develop alors que celle d'à côté fusionne tout directement dans main ? Les deux ont souvent raison, chacune dans son contexte.
Le blocage vient de la façon dont ces stratégies sont présentées : on aligne des modèles, chacun avec son diagramme, sans donner les critères qui permettent de trancher. On retient les noms, rarement les raisons.
Ici, on prend le chemin inverse : on part des situations, du projet perso jusqu'à l'équipe qui déploie plusieurs fois par jour, et on associe à chacune une stratégie avec les réglages GitHub qui la font tenir. Si les bases (commit, branche, push) sont encore récentes, le guide Git pour débutants : le guide pratique pour coder sans stress reprend tout depuis le début.
Le problème : un modèle choisi pour de mauvaises raisons
Prends une équipe de trois développeurs sur une application web. Un seul serveur de production, des mises en ligne plusieurs fois par semaine, aucune ancienne version à maintenir. Elle adopte Git Flow parce que c'est « le standard ». Quelques mois plus tard, develop et main ne contiennent plus la même chose, une branche release est ouverte pour une correction de deux lignes, et chaque correctif urgent doit être fusionné à deux endroits. Le modèle ajoute des étapes sans rien protéger que l'équipe ait besoin de protéger.
Le détail le plus parlant vient de l'auteur de Git Flow lui-même. Vincent Driessen a publié le modèle en janvier 2010 sur son blog. En mars 2020, il a ajouté en tête de l'article d'origine une note de recul : pour une équipe qui livre une application web en continu, il conseille un flux plus simple comme GitHub Flow, et réserve Git Flow aux logiciels versionnés explicitement ou maintenus en plusieurs versions. Sa note se termine sur une consigne sans détour, « Consider your own context », que le reste de l'article applique à la lettre : tiens compte de ton propre contexte.
Le principe : les critères avant le modèle
Une stratégie de branches répond à une seule question : où vit le code pendant qu'il n'est pas encore en production, et comment il y arrive. Pour y répondre, cinq critères suffisent.
- Le nombre de personnes qui poussent du code sur le dépôt.
- Le rythme des mises en production : à chaque fusion, chaque semaine, ou à chaque version numérotée.
- Le nombre de versions à maintenir en parallèle : une seule en ligne, ou une v2 et une v3 installées chez des clients différents.
- La fiabilité de l'intégration continue (CI) : est-ce que des tests automatiques bloquent une fusion qui casse le projet ?
- La possibilité de cacher une fonctionnalité inachevée en production, avec un interrupteur dans le code (un feature flag).
Le critère qui pèse le plus lourd est le troisième. Avec une seule version en ligne, aucune branche permanente n'est nécessaire à côté de main, puisque tout finit au même endroit. Dès qu'il faut corriger la v2 sans embarquer ce qui se prépare pour la v3, chaque version a besoin de sa propre branche, et la stratégie se complique mécaniquement.
Les commandes de l'article utilisent git switch, disponible depuis Git 2.23, plutôt que l'ancien git checkout pour changer de branche. Les réglages GitHub cités sont ceux de la documentation officielle au moment où ces lignes sont écrites, et certains dépendent de ton offre GitHub : c'est précisé à chaque fois.
Cinq situations, cinq façons de brancher
Situation 1 : seul sur un projet perso ou un portfolio
Travailler seul ne dispense pas des branches. Une branche courte par fonctionnalité, fusionnée dans main par une pull request (PR), laisse un historique lisible. Un recruteur qui ouvre ton dépôt voit des PR titrées, décrites, fusionnées une par une, là où un historique de cinquante commits « fix » poussés en direct sur main ne raconte rien.
# Git 2.23 ou plus
git switch -c feature/formulaire-contact
# ... tes commits ...
git push -u origin feature/formulaire-contact
# Ouvre ensuite la PR sur GitHub, relis le diff, fusionne
Relire son propre diff dans l'interface de GitHub avant de fusionner attrape souvent des oublis : un console.log resté là, un fichier de configuration local ajouté par erreur. Active aussi l'option Automatically delete head branches dans les paramètres du dépôt, pour ne pas accumuler des branches mortes. Si tu cherches de quoi remplir ce portfolio, la liste de projets backend pour débutants donne une base de départ.
Situation 2 : petite équipe, une seule production, déploiement à chaque fusion
C'est le terrain de GitHub Flow. Une règle unique : main est toujours déployable. Chaque changement part d'une branche créée depuis main, passe par une PR relue et testée, puis revient dans main, qui part en production.
Cette règle ne tient que si GitHub l'impose. Un ruleset sur main (section Rules des paramètres du dépôt) permet d'exiger une PR avant toute fusion, au moins une approbation, le passage des tests automatiques, et d'interdire le force push. Les rulesets sont disponibles sur les dépôts publics avec l'offre gratuite, et sur les dépôts privés à partir de GitHub Pro ou GitHub Team. Sur un dépôt privé en offre gratuite, main reste donc sans protection, et c'est à l'équipe de s'en souvenir.
Pour garder une branche courte et limiter les conflits, récupère régulièrement ce qui a bougé sur main :
git switch feature/filtre-recherche
git fetch origin
git rebase origin/main
# Le rebase réécrit tes commits : la branche distante doit être remplacée
git push --force-with-lease
--force-with-lease refuse d'écraser la branche distante si quelqu'un d'autre y a poussé entre-temps, ce que --force fait sans prévenir. Sur une branche partagée à plusieurs, cette différence évite de supprimer le travail d'un collègue.
Situation 3 : une recette à valider avant la production
Beaucoup de projets en agence ou en ESN passent par une recette où le client valide avant la mise en ligne. Le réflexe courant consiste à créer une branche par environnement, staging et production, et à fusionner de l'une vers l'autre. Ces branches finissent par diverger : un correctif appliqué directement en production n'est jamais remonté, et la recette teste un code qui ne correspond plus à ce qui tourne en ligne.
Avec GitHub, une autre voie existe : garder une seule branche main et faire porter les étapes par les environnements de GitHub Actions. Le même commit part d'abord en recette, puis en production.
# .github/workflows/deploiement.yml
name: Déploiement
on:
push:
branches: [main]
jobs:
recette:
runs-on: ubuntu-latest
environment: staging
steps:
- run: echo "Build et déploiement vers la recette"
production:
needs: recette
runs-on: ubuntu-latest
environment: production
steps:
- run: echo "Déploiement vers la production"
Sur l'environnement production, la règle Required reviewers met le job en attente jusqu'à ce qu'une personne désignée l'approuve. Le code validé en recette et le code mis en ligne sont alors strictement les mêmes.
Situation 4 : un logiciel versionné, plusieurs versions maintenues
Une bibliothèque publiée, une application installée chez des clients qui ne mettent pas tous à jour au même rythme : ici, plusieurs versions vivent en même temps et doivent recevoir des correctifs séparément. C'est le cas pour lequel Git Flow a été pensé, et il y reste pertinent, comme son auteur le rappelle.
Une variante plus légère suffit souvent : main reçoit le développement courant, et chaque version maintenue a sa branche de support (release/2.x, release/3.x). Un correctif part de la branche de la version concernée, puis il est reporté sur main pour ne pas réapparaître dans la version suivante.
# Correctif sur la version 2
git switch release/2.x
git switch -c fix/export-csv
# ... correction, PR vers release/2.x, fusion ...
# Report du correctif sur main
git switch main
git cherry-pick -x <sha-du-correctif>
# Publication de la version corrigée
git switch release/2.x
git pull
git tag -a v2.4.1 -m "Version 2.4.1"
git push origin v2.4.1
L'option -x ajoute au message du commit la référence du commit d'origine, ce qui permet de retrouver plus tard d'où vient chaque report. Côté GitHub, un ruleset peut cibler toutes les branches de support d'un coup avec un motif comme release/**/*, et les GitHub Releases transforment chaque tag en page de version avec des notes de version générables automatiquement à partir des PR fusionnées.
Le prix à payer est connu : chaque correctif demande au moins deux fusions, et plus il y a de versions maintenues, plus le risque d'en oublier une grandit. Si une seule version est en ligne à la fois, cette complexité ne rapporte rien.
Situation 5 : équipe rodée, CI solide, déploiements fréquents
Le trunk-based development pousse la logique de GitHub Flow au bout : tout le monde intègre son travail dans main au moins une fois par jour, les branches vivent quelques heures, et les fonctionnalités pas encore prêtes partent en production désactivées derrière un interrupteur.
// panier.js (Node.js, modules ES)
import { panierV1, panierV2 } from "./panier-versions.js";
const nouveauPanierActif = process.env.FLAG_NOUVEAU_PANIER === "true";
export function afficherPanier(utilisateur) {
if (nouveauPanierActif) {
return panierV2(utilisateur);
}
return panierV1(utilisateur);
}
Le nouveau panier est fusionné, déployé, mais invisible tant que la variable n'est pas activée. Plus besoin d'une branche qui vit trois semaines en attendant que la fonctionnalité soit terminée.
Quand beaucoup de PR arrivent sur main chaque jour, deux PR testées séparément peuvent casser le projet une fois réunies. La merge queue de GitHub règle ce cas : elle regroupe les PR en file, teste chacune avec la dernière version de main et celles qui la précèdent, et ne fusionne que ce qui passe. Les workflows GitHub Actions doivent alors écouter l'événement merge_group, sans quoi les vérifications ne se lancent jamais et la file reste bloquée.
on:
pull_request:
merge_group:
La merge queue est disponible sur les dépôts publics appartenant à une organisation, et sur les dépôts privés des organisations en GitHub Enterprise Cloud. Un compte personnel n'y a pas accès.
Sans CI fiable, le trunk-based est un piège : chaque fusion ratée atterrit directement dans le code qui part en production, sans branche intermédiaire pour l'arrêter. Mettre en place des tests qui bloquent les fusions défaillantes passe avant le choix de la stratégie, et c'est le cœur de la formation CI/CD avec GitHub Actions.
La grille de décision
Le tableau ne désigne pas de gagnant. Il sert à repérer la colonne qui décrit ton projet aujourd'hui, en sachant qu'elle peut changer quand l'équipe grandit.
| Critère | GitHub Flow | GitHub Flow + environnements | Branches de release / Git Flow | Trunk-based |
|---|---|---|---|---|
| Taille d'équipe | 1 à quelques personnes | Petite à moyenne | Toutes tailles | Toutes tailles, équipe rodée |
| Rythme de mise en prod | À chaque fusion | Après validation en recette | À chaque version numérotée | Plusieurs fois par jour |
| Versions en parallèle | Une | Une | Plusieurs | Une |
| CI requise | Recommandée | Indispensable pour déployer | Recommandée | Non négociable |
| Feature flags | Optionnels | Optionnels | Rarement utiles | Indispensables |
| Branches permanentes | main | main | main + une par version (ou develop) | main |
Merge, squash ou rebase : le bouton en bas de la PR
La stratégie de branches décide où va le code. Le bouton de fusion décide à quoi ressemble l'historique une fois qu'il y est arrivé, et GitHub en propose trois versions.
- Create a merge commit : tous les commits de la branche sont conservés, plus un commit de fusion qui marque le point de jonction. C'est l'équivalent d'un
git merge --no-ff, l'option que Git Flow utilise pour chaque fusion. - Squash and merge : tous les commits de la PR deviennent un seul commit sur la branche cible. Les « wip » et « correction typo » disparaissent de l'historique principal.
- Rebase and merge : les commits sont rejoués un par un sur la branche cible, sans commit de fusion. L'historique reste linéaire, mais GitHub crée toujours de nouveaux identifiants (SHA) pour ces commits.
Le squash convient aux branches courtes de GitHub Flow et du trunk-based : une PR, une modification logique, un commit. Il devient un piège sur une branche qui vit longtemps, et la documentation GitHub le signale : si tu continues à travailler sur une branche après l'avoir fusionnée en squash, les PR suivantes embarquent des commits déjà intégrés, et les mêmes conflits reviennent. Squasher develop vers main dans un Git Flow produit exactement ce problème à chaque version.
Dans les paramètres du dépôt, tu peux désactiver les méthodes que l'équipe n'utilise pas. Ne laisser qu'un seul bouton évite qu'une PR soit squashée et la suivante rebasée selon l'humeur de la personne qui fusionne.
Les pièges qui font dérailler une stratégie
La branche qui vit trois semaines
Quelle que soit la stratégie, plus une branche s'éloigne de sa base, plus la fusion finale est douloureuse : au bout de quelques semaines, d'autres ont renommé des fonctions et déplacé des fichiers que la branche modifie aussi. Découper la fonctionnalité en PR plus petites, ou la cacher derrière un feature flag, coûte moins cher qu'une journée de résolution de conflits.
Le correctif qui ne remonte jamais
Dès qu'il existe plus d'une branche permanente, chaque correctif urgent doit être reporté partout. Oublier une seule fois, et le bug réapparaît à la version suivante, ce qui arrive d'autant plus que l'urgence pousse à fusionner vite et à passer à autre chose. Une case à cocher « reporté sur main » dans le modèle de PR limite la casse.
Le nommage laissé au hasard
Entre test2, nouvelle-branche et modif-julien, personne ne sait ce qui est en cours. Des préfixes simples (feature/, fix/, release/) rendent la liste des branches lisible et permettent de cibler des règles GitHub par motif.
La stratégie que personne n'a écrite
Une stratégie qui n'existe que dans la tête du plus ancien de l'équipe sera mal appliquée par les autres. Quelques lignes dans un fichier CONTRIBUTING.md à la racine du dépôt suffisent : d'où partent les branches, comment on les nomme, quel bouton de fusion utiliser. GitHub met ce fichier en avant quand quelqu'un ouvre une PR.
Changer de stratégie en cours de route
Passer de Git Flow à GitHub Flow se fait sans tout casser si l'on procède dans l'ordre :
- Terminer ou abandonner les branches
releaseethotfixen cours. - Fusionner
developdansmainpour repartir d'une base commune. - Faire de
mainla branche par défaut du dépôt si ce n'était pas le cas. - Changer la branche cible des PR encore ouvertes : GitHub permet de modifier la base d'une PR sans la recréer.
- Mettre à jour les déclencheurs de la CI qui visaient
develop, puis supprimerdevelop.
Prévois un moment calme pour le faire, pas une veille de mise en production. Et préviens l'équipe avant, sinon quelqu'un repartira de develop le lendemain, par habitude.
Pour pratiquer ces manipulations sur un vrai dépôt, la formation Git et GitHub couvre branches, PR et dépôts distants, et la formation CI/CD avec GitHub Actions prend le relais sur les tests et les déploiements qui rendent une stratégie simple tenable. Pour situer Git parmi les autres outils du quotidien côté serveur, le guide des technologies backend donne une vue d'ensemble.