Articles

Un modèle de branchement Git réussi

Posted on

Note de réflexion (5 mars 2020)

Ce modèle a été conçu en 2010, il y a maintenant plus de 10 ans, et pas très longtemps après la naissance de Git lui-même. Au cours de ces 10 ans, git-flow (le modèle de branchement exposé dans cet article) est devenu extrêmement populaire dans de nombreuses équipes de logiciels, au point que les gens ont commencé à le traiter comme une sorte de standard – mais malheureusement aussi comme un dogme ou une panacée.

Au cours de ces 10 ans, Git lui-même a pris le monde d’assaut, et le type de logiciel le plus populaire qui est développé avec Git se déplace davantage vers les applications web – au moins dans ma bulle de filtre. Les applications web sont typiquement livrées en continu, sans retour en arrière, et vous n’avez pas à supporter de multiples versions du logiciel fonctionnant dans la nature.

Ce n’est pas la classe de logiciels que j’avais en tête lorsque j’ai écrit ce blogpost il y a 10 ans. Si votre équipe fait de la livraison continue de logiciels,je vous suggérerais d’adopter un flux de travail beaucoup plus simple (comme GitHubflow) au lieu d’essayer de shoehorn git-flow dans votre équipe.

Si, cependant, vous construisez des logiciels qui sont explicitement versionnés, ou si vous avez besoin de supporter plusieurs versions de votre logiciel dans la nature, alorsgit-flow peut encore être aussi bien adapté à votre équipe qu’il l’a été pour les gens au cours des 10 dernières années. Dans ce cas, veuillez lire la suite.

Pour conclure, rappelez-vous toujours que les panacées n’existent pas. Considérez votre proprecontexte. Ne soyez pas haineux. Décidez par vous-même.

Dans ce billet, je présente le modèle de développement que j’ai introduit pour certains de mes projets (tant au travail que privés) il y a environ un an, et qui s’est avéré très fructueux. Cela fait un moment que j’ai l’intention d’écrire à ce sujet, mais je n’ai jamais vraiment trouvé le temps de le faire de manière approfondie, jusqu’à maintenant. Je ne parlerai pas des détails des projets, simplement de la stratégie de branchement et de la gestion des versions.

Pourquoi git ? ¶

Pour une discussion approfondie sur les avantages et les inconvénients de Git par rapport aux systèmes centralisés de contrôle du code source, voir theweb. Il y a beaucoup de flamewars qui s’y déroulent. En tant que développeur, je préfère Git à tous les autres outils disponibles aujourd’hui. Git a vraiment changé la façon dont les développeurs pensent à la fusion et au branchement.Du monde classique de CVS/Subversion dont je suis issu, la fusion/branchement a toujours été considérée comme un peu effrayante (« attention aux conflits de fusion, ils vous mordent ! ») et quelque chose que vous ne faites que de temps en temps.

Mais avec Git, ces actions sont extrêmement bon marché et simples, et elles sontconsidérées comme l’une des parties essentielles de votre flux de travail quotidien, vraiment. Par exemple,dans les livres sur CVS/Subversion, le branchement et la fusion sont d’abord abordés dans les chapitres ultérieurs (pour les utilisateurs avancés), alors que dans chaque livre Git, ils sont déjà abordés au chapitre3 (les bases).

En conséquence de sa simplicité et de sa nature répétitive, le branchement et la fusion ne sont plus quelque chose à craindre. Les outils de contrôle de version sont censésassister le branchement/fusion plus qu’autre chose.

Assez parlé des outils, passons au modèle de développement. Le modèle que je vais présenter ici n’est essentiellement rien de plus qu’un ensemble de procédures que chaque membre de l’équipe doit suivre afin d’arriver à un processus de développement logiciel géré.

Décentralisé mais centralisé ¶

La configuration de dépôt que nous utilisons et qui fonctionne bien avec ce modèle de branchement,est celle avec un repo central « vérité ». Notez que ce repo est seulement considéré comme le repo central (puisque Git est un DVCS, il n’existe pas de repo central au niveau technique). Nous ferons référence à ce repo par origin, puisque ce nom est familier à tous les utilisateurs de Git.

Chaque développeur tire et pousse vers l’origine. Mais en plus des relations centraliséespush-pull, chaque développeur peut également tirer des modifications d’autres pairs pour former des sous-équipes. Par exemple, cela peut être utile pour travailler ensemble avec deux ou plusieurs développeurs sur une nouvelle fonctionnalité importante, avant de pousser le travail en cours versorigin prématurément. Dans la figure ci-dessus, il existe des sous-équipes d’Alice et Bob,Alice et David, et Clair et David.

Techniquement, cela ne signifie rien de plus que Alice a défini un remote Git,nommé bob, pointant vers le dépôt de Bob, et vice versa.

Les branches principales ¶

Au cœur, le modèle de développement est grandement inspiré des modèles existants à l’extérieur. Le repo central détient deux branches principales avec une durée de vie infinie :

  • master
  • develop

La branche masterorigin devrait être familière à tout utilisateur de Git. Parallèlement à la branche master, il existe une autre branche appelée develop.

Nous considérons origin/master comme la branche principale où le code source deHEAD reflète toujours un état prêt pour la production.

Nous considérons origin/develop comme la branche principale où le code source deHEAD reflète toujours un état avec les derniers changements de développement livrés pour la prochaine version. Certains l’appellent la « branche d’intégration ». C’est de là que sont construits tous les nightly builds automatiques.

Lorsque le code source de la branche develop atteint un point stable et est prêt à être publié, tous les changements doivent être fusionnés à nouveau dans masterd’une manière ou d’une autre, puis étiquetés avec un numéro de version. La façon dont cela est fait en détail sera discutée plus loin.

Par conséquent, chaque fois que des changements sont fusionnés à nouveau dans master, il s’agit d’une nouvelle version de production par définition. Nous avons tendance à être très stricts à ce sujet, de sorte que théoriquement, nous pourrions utiliser un script Git hook pour construire et déployer automatiquement notre logiciel sur nos serveurs de production chaque fois qu’il y a un commit surmaster.

Branches de soutien ¶

Après les branches principales master et develop, notre modèle de développement utilise une variété de branches de soutien pour aider le développement parallèle entre les membres de l’équipe, faciliter le suivi des fonctionnalités, préparer les versions de production et aider à corriger rapidement les problèmes de production en direct. Contrairement aux branches principales, ces branches ont toujours une durée de vie limitée, puisqu’elles seront supprimées à terme.

Les différents types de branches que nous pouvons utiliser sont :

  • Branches de fonctionnalités
  • Branches de versions
  • Branches de correctifs

Chacune de ces branches a un but spécifique et est liée à des règles strictes quant à savoir quelles branches peuvent être leur branche d’origine et quelles branches doivent être leurs cibles de fusion. Nous allons les passer en revue dans une minute.

Ces branches ne sont en aucun cas « spéciales » d’un point de vue technique. Les types de branches sont catégorisés par la façon dont nous les utilisons. Il s’agit bien sûr de vieilles branches Git ordinaires.

Branches de fonctionnalités ¶

Peut se ramifier à partir de :developDoit fusionner à nouveau dans :developConvention de dénomination des branches : tout saufmasterdeveloprelease-*, ouhotfix-*

Les branches de fonctionnalités (ou parfois appelées branches de sujets) sont utilisées pour développer de nouvelles fonctionnalités pour la version à venir ou une version future lointaine. Lorsque l’on commence le développement d’une fonctionnalité, la version cible dans laquelle cette fonctionnalité sera incorporée peut très bien être inconnue à ce moment-là. L’essence d’une branche de fonctionnalité est qu’elle existe tant que la fonctionnalité est en cours de développement, mais qu’elle finira par être fusionnée à nouveau dans develop (pour ajouter définitivement la nouvelle fonctionnalité à la version à venir) ou jetée (en cas d’expérience décevante).

Les branches de fonctionnalité n’existent généralement que dans les dépôts de développeurs, et non dans origin.

Créer une branche de fonctionnalité ¶

Lorsque vous commencez à travailler sur une nouvelle fonctionnalité, créez une branche à partir de la develop branche.

$ git checkout -b myfeature developSwitched to a new branch "myfeature"

Incorporer une fonctionnalité terminée sur develop ¶

Les fonctionnalités terminées peuvent être fusionnées dans la branche develop pour les ajouter définitivement à la prochaine version :

$ git checkout developSwitched to branch 'develop'$ git merge --no-ff myfeatureUpdating ea1b82a..05e9557(Summary of changes)$ git branch -d myfeatureDeleted branch myfeature (was 05e9557).$ git push origin develop

Le drapeau --no-ff fait que la fusion crée toujours un nouvel objet commit, même si la fusion a pu être effectuée avec un fast-forward. Cela évite de perdre des informations sur l’existence historique d’une branche de fonctionnalité et de regrouper tous les commits qui ont ajouté ensemble la fonctionnalité. Comparez:

Dans ce dernier cas, il est impossible de voir à partir de l’historique Git lesquels des objetscommit ont ensemble implémenté une fonctionnalité-vous devriez lire manuellement tous les messages de log. Rétablir une fonctionnalité entière (c’est-à-dire un groupe de commits),est un véritable casse-tête dans cette dernière situation, alors que cela se fait facilement si le drapeau--no-ff était utilisé.

Oui, cela créera un peu plus d’objets commit (vides), mais le gain est bien plus grand que le coût.

Branches de publication ¶

Peut se ramifier à partir de :developDoit fusionner à nouveau dans :developetmasterConvention de nommage des branches :release-*

Les branches de publication prennent en charge la préparation d’une nouvelle version de production. Elles permettent de mettre les points sur les i et les barres sur les t à la dernière minute. En outre, elles permettent de corriger des bogues mineurs et de préparer les méta-données d’une version (numéro de version, dates de construction, etc.). En effectuant tout ce travail sur une branche de release, la developbranche est libérée pour recevoir les fonctionnalités de la prochaine grosse release.

Le moment clé pour brancher une nouvelle branche de release à partir de develop est lorsqueendevelop reflète (presque) l’état souhaité de la nouvelle release. Au moins toutes les fonctionnalités qui sont ciblées pour la version à construire doivent être fusionnées dansdevelop à ce moment-là. Toutes les fonctionnalités ciblées sur les futures versions ne le peuvent pas – elles doivent attendre que la branche de la version soit branchée.

C’est exactement au début d’une branche de la version que la version à venir se voit attribuer un numéro de version – pas plus tôt. Jusqu’à ce moment, la developbranche reflétait les changements pour la « prochaine version », mais on ne sait pas si cette « prochaine version » deviendra finalement 0.3 ou 1.0, jusqu’à ce que la branche release soit démarrée. Cette décision est prise au démarrage de la branche de publication et est exécutée par les règles du projet sur le saut de numéro de version.

Création d’une branche de publication ¶

Les branches de publication sont créées à partir de la branche develop. Par exemple, disons que la version 1.1.5 est la version de production actuelle et que nous avons une grosse releasecoming. L’état de develop est prêt pour la « prochaine version » et nous avons décidé que cela deviendra la version 1.2 (plutôt que 1.1.6 ou 2.0). Donc, webranch off et donner à la branche release un nom reflétant le nouveau numéro de version:

$ git checkout -b release-1.2 developSwitched to a new branch "release-1.2"$ ./bump-version.sh 1.2Files modified successfully, version bumped to 1.2.$ git commit -a -m "Bumped version number to 1.2" Bumped version number to 1.21 files changed, 1 insertions(+), 1 deletions(-)

Après avoir créé une nouvelle branche et basculé dessus, nous faisons sauter le numéro de version.Ici, bump-version.sh est un script shell fictif qui modifie certains fichiers dans la copie de travail pour refléter la nouvelle version. (Il peut bien sûr s’agir d’un changement manuel – le point étant que certains fichiers changent). Ensuite, le numéro de la version bumpée est commit.

Cette nouvelle branche peut exister là pendant un certain temps, jusqu’à ce que la version puisse être déployée définitivement. Pendant ce temps, les corrections de bogues peuvent être appliquées dans cette branche(plutôt que sur la develop branche). L’ajout de nouvelles fonctionnalités importantes est strictement interdit ici. Elles doivent être fusionnées dans develop, et donc, attendre la prochaine grosse version.

Finition d’une branche de publication ¶

Lorsque l’état de la branche de publication est prêt à devenir une vraie version, certaines actions doivent être effectuées. Tout d’abord, la branche release est fusionnée dansmaster (puisque chaque commit sur master est une nouvelle release par définition, rappelez-vous). Ensuite, ce commit sur master doit être balisé pour faciliter les références futures à cette version historique. Enfin, les modifications apportées sur la branche release doivent être fusionnées à nouveau dans develop, afin que les futures versions contiennent également ces corrections de bogues.

Les deux premières étapes dans Git:

$ git checkout masterSwitched to branch 'master'$ git merge --no-ff release-1.2Merge made by recursive.(Summary of changes)$ git tag -a 1.2

La release est maintenant faite, et étiquetée pour référence future.

Modification : Vous pourriez tout aussi bien utiliser les drapeaux -s ou -u <key> pour signer votre tag de manière cryptographique.

Pour conserver les modifications apportées dans la branche release, nous devons cependant les fusionner à nouveau dans develop. Dans Git:

$ git checkout developSwitched to branch 'develop'$ git merge --no-ff release-1.2Merge made by recursive.(Summary of changes)

Cette étape pourrait bien conduire à un conflit de fusion (probablement même, puisque nous avons changé le numéro de version). Si c’est le cas, corrigez-le et faites un commit.

Maintenant, nous avons vraiment terminé et la branche release peut être supprimée, puisque nous n’en avons plus besoin :

$ git branch -d release-1.2Deleted branch release-1.2 (was ff452fe).

Branches de correction ¶

Peut bifurquer de :masterDoit fusionner à nouveau dans :developetmasterConvention de dénomination des branches :hotfix-*

Les branches hotfix sont très proches des branches release dans la mesure où elles sont également destinées à préparer une nouvelle version de production, bien que non planifiée. Elles découlent de la nécessité d’agir immédiatement sur un état indésirable d’une version de production en direct. Lorsqu’un bogue critique dans une version de production doit être résoluimmédiatement, une branche de correctif peut être branchée à partir de l’étiquette correspondante sur la branche maîtresse qui marque la version de production.

L’essence est que le travail des membres de l’équipe (sur la branche develop) peut se poursuivre, tandis qu’une autre personne prépare un correctif de production rapide.

Création de la branche de correctif ¶

Les branches de correctif sont créées à partir de la branche master. Par exemple, disons que la version1.2 est la version de production actuelle fonctionnant en direct et causant des problèmes en raison d’un bogue grave. Mais les changements sur develop sont encore instables. Nous pouvons alors bifurquer d’une branche hotfix et commencer à corriger le problème :

$ git checkout -b hotfix-1.2.1 masterSwitched to a new branch "hotfix-1.2.1"$ ./bump-version.sh 1.2.1Files modified successfully, version bumped to 1.2.1.$ git commit -a -m "Bumped version number to 1.2.1" Bumped version number to 1.2.11 files changed, 1 insertions(+), 1 deletions(-)

Ne pas oublier de faire remonter le numéro de version après avoir bifurqué !

Puis, corriger le bug et commiter le correctif dans un ou plusieurs commits séparés.

$ git commit -m "Fixed severe production problem" Fixed severe production problem5 files changed, 32 insertions(+), 17 deletions(-)

Finir une branche de correction ¶

Une fois terminée, la correction du bogue doit être fusionnée à nouveau dans master, mais doit également être fusionné dans develop, afin de garantir que le correctif soit également inclus dans la prochaine version. Ceci est complètement similaire à la façon dont les releasebranches sont terminées.

D’abord, mettez à jour master et balisez la release.

$ git checkout masterSwitched to branch 'master'$ git merge --no-ff hotfix-1.2.1Merge made by recursive.(Summary of changes)$ git tag -a 1.2.1

Mise à jour : vous pourriez aussi bien utiliser les drapeaux -s ou -u <key> pour signer votre tag de manière cryptographique.

Puis, incluez également le correctif dans develop :

$ git checkout developSwitched to branch 'develop'$ git merge --no-ff hotfix-1.2.1Merge made by recursive.(Summary of changes)

La seule exception à la règle ici est que, lorsqu’une branche release existe actuellement, les modifications du correctif doivent être fusionnées dans cette branche release, au lieu de develop. La rétro-fusion du correctif dans la branche release aboutira éventuellement à la fusion du correctif dans develop également, lorsque la branche release sera terminée. (Si le travail dans develop nécessite immédiatement ce correctif et ne peut pas attendre que la branche release soit terminée, vous pouvez sans risque fusionner le correctif dans develop maintenant déjà aussi.)

Enfin, supprimez la branche temporaire:

$ git branch -d hotfix-1.2.1Deleted branch hotfix-1.2.1 (was abbe5d6).

Résumé ¶

Bien qu’il n’y ait rien de vraiment choquant de nouveau dans ce modèle de branchement, la figure de la « grande image » avec laquelle ce post a commencé s’est avérée être formidablement utile dans nos projets. Elle forme un modèle mental élégant, facile à comprendre et permet aux membres de l’équipe de développer une compréhension commune des processus de branchement et de libération.

Une version PDF de haute qualité de la figure est fournie ici. Allez-y et accrochez-la au mur pour pouvoir la consulter rapidement à tout moment.

Mise à jour : Et pour tous ceux qui l’ont demandé : voici la clégitflow-model.src.key de l’image du diagramme principal (Apple Keynote).


Git-branching-model.pdf

Autres articles sur ce blog

  • Les outils de puissance de Git à utiliser au quotidien
  • Une introduction aux décodeurs
  • La dette technique est une vraie dette
  • Beauce code
  • Beauce carte

.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *