Aller au contenu

Feature Branch Workflow

Le Feature Branch Workflow est un workflow Git dans lequel chaque fonctionnalité est développée dans sa propre branche, puis intégrée à la branche principale main après une relecture, au moyen d'une merge request (MR).

La semaine A05 en a présenté le principe (voir Workflow Git). Cette page détaille son fonctionnement technique : les commandes Git, les étapes sur GitLab et la publication des versions.

Ce workflow est très répandu pour les applications web livrées en continu. GitHub le décrit sous le nom de GitHub flow (avec des pull requests, l'équivalent des merge requests de GitLab).

Source : GitHub Docs - GitHub flow

Principe

Une branche principale toujours stable

  • Une seule branche permanente : main.
  • main contient uniquement du code terminé, relu et fonctionnel → elle peut être déployée en production à tout moment.
  • Personne ne fait de commit directement dans main : GitLab l'interdit si la branche est protégée (voir Protéger la branche main).

Une branche par fonctionnalité

  • Chaque fonctionnalité ou correction est développée dans une branche dédiée, créée depuis main (ex. feature/blog-joel.dacomo, fix/footer-color).
  • La branche a une durée de vie courte : quelques heures à quelques jours. Elle est supprimée dès qu'elle est fusionnée.
  • Tant que le travail n'est pas fusionné, il n'a aucun effet sur main ni sur le travail des autres.

La merge request

Une merge request est une demande, faite sur GitLab, de fusionner une branche (la branche source) dans une autre (la branche cible, ici main).

Elle permet

  • De relire les modifications (onglet Changes) et de les commenter ligne par ligne.
  • De corriger le code avec de nouveaux commits sur la même branche : la MR se met à jour automatiquement à chaque push.
  • D'approuver les modifications (Approve), puis de les fusionner (Merge) avec un clic.
  • De garder une trace de la discussion, même après la suppression de la branche.

Analogie

Une merge request ressemble à la relecture d'un rapport avant son envoi : vous rédigez votre partie dans un brouillon (la branche), un collègue la relit et annote les passages à corriger (les commentaires), vous corrigez (nouveaux commits), puis il donne son accord et votre partie est ajoutée au document officiel (la fusion dans main).

Source : GitLab Docs - Merge requests

Comparaison avec Gitflow

Critère Gitflow Workflow Feature Branch Workflow
Branches permanentes main et develop main uniquement
Branches temporaires feature/…, release-…, hotfix-… feature/…, fix/…
Intégration d'une branche Merge local, puis push de develop Merge request relue, puis fusion sur GitLab
Relecture du code Non prévue par le workflow Au coeur du workflow (merge request)
Publication d'une version Branche release-… fusionnée dans main et develop Tag sur un commit de main
Adapté à Logiciels publiés en plusieurs versions maintenues en parallèle Applications web livrées en continu (une seule version en production)

Note

Gitflow n'est pas "faux" : il reste adapté aux logiciels dont plusieurs versions doivent être maintenues en même temps (ex. version 2.x et 3.x d'une bibliothèque). Pour un site web, qui n'a qu'une seule version en production, develop et les branches release ajoutent surtout des étapes et des risques de conflit.

Source : Vincent Driessen - A successful Git branching model (Note of reflection)

Cycle de vie d'une fonctionnalité

Les commandes ci-dessous ont toutes un équivalent dans IntelliJ IDEA (menu Git et fenêtre Git). Elles utilisent git switch, une commande plus récente que git checkout et dédiée uniquement au changement de branche (git checkout, vu au TP05.2, fonctionne aussi).

main       A---B-----------------M
                \               /
feature/x        C-----D-----E
                 |     |     |
              commits, push, relecture et corrections

Créer la branche

  1. Mettez à jour la branche main locale :

    git switch main
    git fetch
    git rebase origin/main
    
  2. Créez la branche de la fonctionnalité depuis main et basculez dessus :

    git switch -c feature/blog-joel.dacomo
    

    → Equivalent de git checkout -b feature/blog-joel.dacomo.

Source : Git - Reference (git switch)

Développer et pousser

  1. Faites des commits comme d'habitude : un ensemble logique de modifications par commit, avec un message qui respecte les règles vues en cours.
  2. Envoyez la branche sur origin dès les premiers commits, pour la sauvegarder et la rendre visible :

    git push -u origin feature/blog-joel.dacomo
    
    • L'option -u (--set-upstream) relie la branche locale à la branche origin/feature/blog-joel.dacomo : les push suivants se font simplement avec git push.

Source : Git - Reference (git push, --set-upstream)

Ouvrir une merge request

Sur GitLab, dans le projet : Code → Merge requests → New merge request.

  1. Choisissez la branche source (feature/blog-joel.dacomo) et la branche cible (main).
  2. Rédigez un titre clair et une description : ce que fait la MR, comment la tester, ce qui reste à faire.
    • Si le projet contient un modèle de MR (fichier .gitlab/merge_request_templates/Default.md de la branche main), GitLab pré-remplit la description avec son contenu, par exemple une liste de contrôles à cocher.
  3. Indiquez la personne qui relit votre travail dans le champ Reviewer.
  4. Laissez cochée l'option de suppression de la branche source après la fusion.

Merge request en brouillon (Draft)

Vous pouvez ouvrir la MR avant d'avoir terminé, en la marquant comme brouillon (Mark as draft, ou préfixe Draft: dans le titre). Une MR en brouillon ne peut pas être fusionnée : elle sert à montrer l'avancement et à demander un avis tôt. Retirez le statut de brouillon (Mark as ready) quand le travail est prêt à être relu.

Source : GitLab Docs - Draft merge requests

Source : GitLab Docs - Description templates

Relire et corriger

Personne qui relit (reviewer)

  • Récupère la branche pour tester le résultat : git fetch, puis git switch feature/blog-joel.dacomo.
  • Lit les modifications dans l'onglet Changes de la MR et laisse des commentaires sur les lignes concernées.
  • Approuve la MR (Approve) quand les corrections sont faites.

Auteur de la MR

  • Corrige dans de nouveaux commits sur la même branche, puis git push : la MR se met à jour automatiquement.
  • Répond aux commentaires et marque les discussions comme résolues (Resolve thread).

Mettre à jour la branche

Pendant que vous travaillez, d'autres MR sont fusionnées : main avance et votre branche est "en retard". Avant la fusion, rejouez vos commits par-dessus la version actuelle de main avec un rebase :

Avant le rebase                    Après le rebase

main      A---B---F---G            main      A---B---F---G
               \                                          \
feature/x       C---D              feature/x               C'---D'
  1. Mettez à jour votre branche :

    git fetch
    git switch feature/blog-joel.dacomo
    git rebase origin/main
    
  2. En cas de conflit, Git interrompt le rebase sur le commit qui pose problème et place des marqueurs de conflit (<<<<<<<, =======, >>>>>>>) dans les fichiers concernés :

    1. Ouvrez chaque fichier en conflit (ou utilisez la fenêtre de résolution des conflits d'IntelliJ IDEA), gardez les modifications des autres et les vôtres, et supprimez les marqueurs.
    2. Indiquez à Git que le conflit de ce fichier est résolu : git add <fichier>.
    3. Une fois tous les fichiers résolus, reprenez le rebase : git rebase --continue.
      • Git recrée le commit avec le contenu corrigé, puis rejoue le commit suivant : d'autres conflits peuvent encore apparaître.
    4. En cas de doute, git rebase --abort annule tout et remet la branche dans son état d'avant le rebase.
  3. Envoyez la branche réécrite :

    git push --force-with-lease
    

Si aucun conflit n'est possible, GitLab propose aussi un bouton Rebase directement dans la MR.

Pourquoi git add pendant un rebase ?

Pendant un rebase, git add ne prépare pas un nouveau commit : il marque le conflit du fichier comme résolu, en plaçant sa version corrigée dans l'index (la staging area, voir Git).

  • Tant qu'un fichier en conflit n'a pas été ajouté à l'index, Git le considère comme non résolu et git rebase --continue refuse de continuer.
  • C'est le contenu de l'index que git rebase --continue utilise pour recréer le commit interrompu (ex. C' dans le schéma ci-dessus).

Analogie : le git add revient à dire à Git "j'ai fini de corriger ce fichier, tu peux reprendre".

Source : Git - Reference (git rebase)

Pourquoi un push forcé, et pourquoi --force-with-lease

Le rebase crée de nouveaux commits (C' et D', avec de nouveaux identifiants) qui remplacent C et D. origin/feature/blog-joel.dacomo contient encore les anciens commits : un git push normal est refusé, car il ferait "disparaître" des commits de origin.

  • --force remplace la branche sur origin sans aucun contrôle, même si quelqu'un y a poussé des commits entre-temps.
  • --force-with-lease ne remplace la branche que si elle est encore dans l'état que vous connaissez (celui de votre dernier fetch). Sinon, le push est refusé et rien n'est perdu.

🚨 Réécrire l'historique est acceptable uniquement sur votre propre branche feature, sur laquelle personne d'autre ne travaille. Jamais de push forcé sur main ou sur une branche partagée : vous effaceriez le travail des autres (voir TP05.2).

Source : Git - Reference (git push, --force-with-lease)

Fusionner

Quand la MR est approuvée et à jour, la personne autorisée clique sur Merge dans GitLab. La fusion a lieu sur le serveur GitLab : personne ne fait de merge local dans main.

GitLab supprime alors la branche source sur origin (si l'option est cochée) et la MR passe à l'état Merged.

Nettoyer

Après la fusion, mettez à jour votre dépôt local et supprimez la branche locale :

git switch main
git fetch --prune
git rebase origin/main
git branch -d feature/blog-joel.dacomo
  • --prune supprime les références locales (origin/feature/…) des branches qui n'existent plus sur origin.
  • git branch -d refuse de supprimer une branche dont les commits n'ont pas été fusionnés.
    • Après un rebase fait par GitLab (bouton Rebase), vos commits locaux ont d'autres identifiants que ceux de main : vérifiez dans l'historique que votre travail est bien dans main, puis utilisez git branch -D pour forcer la suppression.

Sources :

Méthodes de fusion dans GitLab

La méthode de fusion est choisie pour tout le projet (Settings → Merge requests → Merge method).

Merge commit

  • GitLab crée toujours un commit de fusion, comme git merge --no-ff.
  • La branche peut être fusionnée même si elle est en retard sur main.
main      A---B---F-------M
               \         /
feature/x       C-------D

Merge commit with semi-linear history

  • GitLab crée un commit de fusion, mais seulement si la branche est à jour par rapport à main : sinon, il faut d'abord faire un rebase.
  • Chaque fonctionnalité reste visible comme un groupe de commits, et elle a été testée sur la version actuelle de main.
main      A---B---F-----------M
                   \         /
feature/x           C'------D'

Fast-forward merge

  • Pas de commit de fusion : les commits de la branche sont ajoutés directement à la suite de main (historique linéaire).
  • La branche doit aussi être à jour par rapport à main.
main      A---B---F---C'---D'

Squash

L'option squash regroupe tous les commits de la branche en un seul commit au moment de la fusion. Elle simplifie l'historique, mais fait disparaître le détail des commits : dans ce cours, où la qualité de chaque commit est évaluée, elle n'est pas utilisée.

Sources :

Protéger la branche main

Une branche protégée est une branche sur laquelle GitLab limite les actions possibles. La configuration se fait dans Settings → Repository → Branch rules (rôle Maintainer nécessaire).

Configuration habituelle pour ce workflow

  • Allowed to push and merge → No one : personne ne peut faire de push directement sur main.
  • Allowed to merge → Developers + Maintainers : les membres du projet peuvent fusionner une MR.
  • Allowed to force push → désactivé : le push forcé est interdit sur main.

La seule façon de modifier main est une merge request : la règle "pas de commit direct dans main" est appliquée par GitLab, et non plus seulement par la discipline de chacun.

Source : GitLab Docs - Protected branches

Approbation obligatoire

Dans les éditions Premium et Ultimate de GitLab (c'est le cas du GitLab de l'école), on peut aussi exiger une approbation avant toute fusion (Settings → Merge requests → Merge request approvals) :

  • Règle All eligible users avec Approvals required → 1 : le bouton Merge reste bloqué tant que la MR n'a pas été approuvée.
  • Prevent approval by merge request creator (author) : l'auteur ne peut pas approuver sa propre MR.
  • Remove all approvals lorsqu'un commit est ajouté (facultatif) : après chaque correction ou rebase, la MR doit être approuvée à nouveau.

Sources :

Versions et releases

Sans branche release, une version correspond simplement à un commit de main, marqué par un tag.

Changelog

  • En haut du fichier CHANGELOG.md, une section ## [Unreleased] regroupe les modifications pas encore publiées.
  • Chaque merge request ajoute sa ligne dans cette section (sous ### Added, ### Changed, ### Fixed…) : le changelog est relu avec le code.
  • Au moment de publier, la section [Unreleased] devient la section de la nouvelle version (ex. ## [0.5.0] - 2026-11-05), et une nouvelle section [Unreleased] vide est créée au-dessus. Comme main est protégée, cette modification passe elle aussi par une MR.
## [Unreleased]

### Added

- Blog page of Joël Dacomo
- Blog page of …

Source : Keep a Changelog - Keep a Changelog (2.0.0)

Tags

Un tag est une étiquette permanente posée sur un commit précis, par exemple pour marquer une version publiée.

  • Tag annoté (-a) : contient l'auteur, la date et un message → à utiliser pour les versions.
  • Tag léger (sans -a) : simple nom donné à un commit, pour un usage personnel ou temporaire.
git switch main
git fetch
git rebase origin/main
git tag -a v0.5.0 -m "Release 0.5.0"
git push origin v0.5.0
  • Le tag est posé sur le dernier commit de main, après la fusion de la MR qui met à jour le changelog.
  • Un git push ordinaire n'envoie pas les tags : il faut les envoyer explicitement.
  • Le préfixe v (v0.5.0) est une convention courante pour les noms de tags (le numéro de version lui-même reste 0.5.0).

Sources :

Releases GitLab

Une release GitLab est une page qui présente une version publiée : notes de version (ex. reprises du changelog) et archives du code source à télécharger.

  • Elle repose sur un tag Git : on la crée dans Deploy → Releases → New release, en choisissant le tag (ou en le créant à cette occasion).
  • Le rôle Developer suffit pour créer une release.

Source : GitLab Docs - Releases

Corrections

Il n'y a pas de branche hotfix : une correction est une modification comme une autre.

  1. Branche fix/… créée depuis main (ex. fix/footer-color).
  2. Merge request, relecture, fusion.
  3. Si la correction doit être publiée seule : nouvelle version avec le numéro de correctif incrémenté (ex. 0.5.0 → 0.5.1).

Commandes Git de ce workflow

Commande Description
git switch <branche> Bascule sur une branche existante
git switch -c <branche> Crée une branche depuis la branche actuelle et bascule dessus
git push -u origin <branche> Envoie une nouvelle branche sur origin et relie la branche locale à celle-ci
git rebase origin/main Rejoue les commits de la branche actuelle par-dessus la dernière version de main
git push --force-with-lease Remplace la branche sur origin après un rebase, seulement si personne n'y a poussé entre-temps
git fetch --prune Récupère les nouveautés de origin et oublie les branches supprimées sur origin
git branch -d <branche> Supprime une branche locale déjà fusionnée
git tag -a <tag> -m "<message>" Crée un tag annoté sur le commit actuel
git push origin <tag> Envoie un tag sur origin

Bonnes pratiques

  • Une merge request = une fonctionnalité ou une correction : de petites MR sont plus faciles et plus rapides à relire.
  • Relisez vous-même votre MR (onglet Changes) avant de demander une relecture.
  • Mettez souvent votre branche à jour avec main : plus la branche vit longtemps, plus les conflits sont nombreux.
  • Ne fusionnez pas une MR qui n'a pas été relue, même si GitLab le permet.
  • Supprimez les branches fusionnées, sur origin et en local.