Journal Git workflow, rebase, conflits et rôle d'intégrateur

Posté par (page perso) . Licence CC by-sa
Tags :
35
2
déc.
2014

'jour 'nal

Depuis quelques temps, à l'occasion d'un nouveau projet (au boulot) avec mes collègues j'ai mis en place un nouveau workflow git.
Vous savez, un truc qui défini comment on gère les branches, les rebases, les fusions, le nommage des branches, etc.

En gros, le workflow est le suivant :

  • tout est réalisé dans des branches
    • les branches sont préfixées (exemple refactor/copter_param, feature/fences, bug/landing) pour les retrouver facilement
    • aucun commit directement dans master
    • pas de branches de prod, maintenance, etc
  • master est la branche stable depuis laquelle on crée les versions
  • une branche d'intégration existe pour faciliter les tests (j'y reviens)
  • les branches sont toujours rebasées depuis integ puis fusionnées en "no fast forward"
  • une fois les tests validés dans integ, on merge dans master (soit en fast forward si tout est bon à prendre, soit en réintégrant branche par branche)

En gros tout ceci a pour objectif principal d'avoir un historique toujours très lisible. L'ensemble des modifications liées à une fonctionnalité est très lisible. Il est "facile" (autant que possible) de supprimer une fonctionnalité (il suffit d'inverser un commit de merge). (Si vous voulez plus de détails sur le pourquoi du comment c'est par ici)

Dans un monde idéal c'est super. Franchement. Avant on avait un historique, comment dire… horrible, des merges dans tous les sens, etc. Aujourd'hui on a un truc lisible :

Avant - Après

Bon c'est bien joli tout ça, mais pourquoi tu nous racontes ça alors ?

Le problème c'est qu'on est pas toujours dans un monde idéal.

En premier lieu, je bosse dans un secteur spécial : c'est du code embarqué pour des 'ti nappareils avec des hélices qui peuvent voler dans le ciel. L'une des conséquences (associée à une base de code, comment dire poliment…, genre si j'attrape le gars qui l'a écrit je lui fais réécrire le noyau linux en whitespace avec un écran monochrome) c'est que parfois nos tests unitaires (et autres) passent sur notre intégration continue, et pour autant les essais en vol ne sont pas concluant. Ça introduit une certaine latence entre le moment où on développe, le moment où on passe en CI et le moment où on valide réellement un développement. D'où la notion de branche d'intégration. La branche d'intégration est une version "à tester" qui, si tout se passe bien, peut devenir stable (master). Mais qui si ça ne se passe pas bien peut être supprimée et refaite suivant les résultats. Evidemment si on pouvait jouer des tests sur toutes les branches et être certains du résultat, il n'y aurait pas de problème ;-)

Le problème résultant c'est que parfois il faut "rouvrir" les branches (enlever le commit de fusion), modifier la branche (corriger), re-fusionner. Et c'est quand même assez fastidieux.

Et l'autre problème est que, pour des questions de lisibilité, on utilise beaucoup le rebase. Oui mais ça veut parfois dire des conflits un peu compliqués et surtout fastidieux. Même avec git rerere.

Pour ce genre de raisons, une personne est aujourd'hui dédiée à la fusion dans les branches d'intégration et master. Tous les autres bossent dans leurs branches. Ça simplifie un peu le boulot global, mais c'est pas terrible.

Alors j'en viens à vous questionner, chers linuxfriens adeptes de Git (mais ça marche aussi avec Mercurial).

Vous arrive-t-il de bosser avec des rôles d'intégrateurs / fusionneurs Git ?
Comment éviter ce type de situation ? Abandonner le rebase au prix d'un historique moins lisible ?
Est-il encore temps de retourner sous svn ?

  • # Kernel

    Posté par . Évalué à 5.

    Vous arrive-t-il de bosser avec des rôles d'intégrateurs / fusionneurs Git ?

    C'est pas typiquement ce que font les développeurs du noyau linux ?

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Kernel

      Posté par (page perso) . Évalué à 2.

      Ha oui, c'est vrai. Mais d'un côté on est sur quelque chose de bien plus gros. Là c'est une équipe de moins de 10 devs. Et assez inhabituel pour moi de bosser de la sorte. La seule fois c'était dans un autre taff où c'était la seule solution pour éviter que certains poussent leurs modifs n'importe comment sans jamais les tester :/

  • # Pourquoi ?

    Posté par (page perso) . Évalué à 9.

    Dans un monde idéal c'est super. Franchement. Avant on avait un historique, comment dire… horrible, des merges dans tous les sens, etc.

    C'est à mon avis là que se situe le problème : pas un problème technique, mais une problème de compréhension. Git force certains processus, c'est sûr, mais il en facilite d'autres. En particulier, ses capacités de fusion de modifications favorisent les processus qui utilisent des branches multiples et des fusions.

    Donc, ici, ma question est : pourquoi le fait d'avoir plusieurs branches, et des fusions régulières entre ces branches, te semble-t-il problématique ? Parmi les projets logiciels les plus compliqués en terme de taille et d'organisation humaine, il y a le noyau Linux, qui est maintenu sous la forme de tas de branches et de fusions, et visiblement ça marche, donc je ne pense pas que ce soit un problème en tant que tel.

    À mon avis, il serait plus utile de cesser de faire la grimace devant un historique qui présente des branches multiples, et considérer cela comme un fait sans conséquences.

    En particulier, dans la vraie vie :

    pas de branches de prod, maintenance, etc

    Il n'y a pas vraiment de moyen d'échapper à l'utilisation de branches de maintenances. C'est bête, mais quand tu as sorti Logiciel v2.3, et que tu développes sur les v2.4, si quelqu'un signale un bug qui doit être corrigé sur la v2.3, on ne peut pas faire cette correction sur la seule branche de travail de la v2.4, et il faut donc introduire une branche de maintenance. Et pour que cette correction soit appliquée aussi à la v2.4, Git fournit une solution : créer une branche de correction, basée sur le dernier commit commun à la v2.3 et à la v2.4, y effectuer cette correction, puis la fusionner dans les branches de la v2.3 et de la v2.4, comme ça :

               v2.3  v2.3.1
                ↓     ↓
              ,-*-----*  ← version/2.3
             /       /
            /--*----<    ← bug/truc
           /         \   
    *--*--*--*--*-----*  ← master

    Comment éviter ce type de situation ? Abandonner le rebase au prix d'un historique moins lisible ?

    À mon avis, oui.

    • [^] # Re: Pourquoi ?

      Posté par (page perso) . Évalué à 4.

      il y a le noyau Linux, qui est maintenu sous la forme de tas de branches et de fusions, et visiblement ça marche

      Oui et non. Oui en apparence, dans le détail il y a des règles qui sont entre autre "ne te fusionne pas avec l'upstream n'importe quand / comment" (http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html)

      À mon avis, il serait plus utile de cesser de faire la grimace devant un historique qui présente des branches multiples, et considérer cela comme un fait sans conséquences.

      Ça c'est vrai, à une condition : que tu ne cherches jamais à relire ton historique. Un gestionnaire de source c'est un outil au quotidien pour partager le boulot mais c'est aussi un historique qui se doit d'être clair et compréhensible. Il m'est déjà arrivé de passer 1,5 jour uniquement à chercher le commit ayant entrainé une régression. L'historique contenait tellement de merges dans tous les sens que c'en était illisible et qu'un bisect était horrible.
      Et malheureusement revenir dans l'historique est une pratique relativement fréquente, surtout dans notre cas particulier qui est de ne pas pouvoir valider à 100% un code sans "prendre la voiture un jour de beau temps, faire quelques dizaines de bornes, tester sur du matériel réel, rentrer". Ce qui introduit de la latence. Et comme il y a latence on bosse déjà sur la suite et, si le point en test n'est pas passé, alors on reviens dessus pour le corriger.

      Maintenant s'il y a moyen d'avoir un historique lisible avec des branches à tout vas, je veux bien ;-)

      Il n'y a pas vraiment de moyen d'échapper à l'utilisation de branches de maintenances. […]

      Attention, je ne dis pas qu'il n'y en aura jamais. Juste que là maintenant il n'y en a pas. master est notre branche stable et en quelque sorte il n'y a pas de maintenance à faire puisqu'on est en plein dans une phase de dev et non d'exploitation.

      Et même sans ça, en vrai si il est possible de se passer de ces branches, mais c'est des choix différents qui sont d'avoir quelque chose qui s'approche plus de la "rolling release".

      • [^] # Workflow similaire

        Posté par . Évalué à 4.

        J'utilise un workflow plutot similaire mais sans rebase en soi.

        Mon workflow a moi est le même, on crée des branches tout le temps (pour chaques features, bugs …)
        Par contre au niveau merge on fait du squash + cherrypick de la branche à merger. Ensuite on utilise gerrit qui à la particularité de mettre un ID unique dans le message de commit donc depuis la branches d'intégration ou la master si on veut retrouver le log complet d'une feature, on regarde l'ID dans le commit de message et on retrouve la branche correspondante.

        On ne rebase jamais, on ne fusionne jamais les branches, on cherry pick. Si jamais on a intégrer un changement sur la master qui doit etre retiré on le revert tout simplement. (Ce qui normalement n'arrive jamais puisque la branche d'intégration sert à éviter ça).

        Peut etre que ça peut te convenir comme workflow.

        • [^] # Re: Workflow similaire

          Posté par (page perso) . Évalué à 2.

          Intéressant comme approche.

          Par contre ça veut dire que vous gardez en plus toutes les branches ? Ça devient pas un peu vite le bordel ?
          On bosse par itération de deux semaines, on doit créer genre 5 à 10 branches par itération. Si on prend un peu de recul ça va vite devenir ingérable, non ?

          Mais en tout cas je garde l'idée dans un coin.

          • [^] # Re: Workflow similaire

            Posté par . Évalué à 4.

            oui on garde toutes les branches, après on se base sur gerrit qui fournit quelques avantages. Toutes ses branches sont des branches de revue gerrit (gerrit crée automatique une nouvelle branche lorsque tu pousse un commit qui a un nouveau gerrit ID dans son commit de message) et c'est des branches spéciales. Spéciales dans le sens quels sont dans un refspec git séparé. Donc oui tu as toutes les branches sur le serveur mais par défaut tu ne les vois jamais. (Pas dans le refspec par défaut).
            Comme les branches de revue sont très petites, le log linéaire dans la branche de master suffit la plupart du temps. Si y a une besoin précis, faut aller récupérer la bonne branche (avec le gerrit ID c'est vraiment facile) et la on a accès à tous les commits de la feature mais aussi à tous les commentaires de la revue de code gerrit, les logs de l'intégration continue etc…

            La seul limitation de ce workflow c'est si tu as plusieurs "master" (si tu gère plusieurs versions en parallèle genre une branche v1.x et une branche v2.x), si tu veux backporter un commit d'une branches à l'autre on préfère passer par une nouvelle branche de revue et donc changer le gerrit ID et on inclue manuellement l'ancien gerrit ID dans le message de commit. C'est pas obligatoire mais ça permet de si retrouver plus facilement.

            Utiliser se workflow sans gerrit demande à mon avis quelques adaptations. Gérer les git refspec à la main ça peut etre assez lourd (quoique à y réfléchir c'est pas si compliqué) et tous avoir dans le même refspec devient vite le bordel.

            • [^] # Re: Workflow similaire

              Posté par (page perso) . Évalué à 2.

              Ha ok, j'avais pas pensé à cette histoire de refspec. C'est vrai que ça peut être une solution.
              Pour le moment il n'y a pas plusieurs master donc pas de problème pour ça.

      • [^] # Re: Pourquoi ?

        Posté par . Évalué à 3.

        Maintenant s'il y a moyen d'avoir un historique lisible avec des branches à tout vas, je veux bien ;-)

        git config --global alias.lg 'log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit'
        git lg --first-parent master
        git lg --first-parent --all
        

        En pratique le --first-parent aide pas mal dans les recherches pour que ca soit moins foutoir.

        Après si tu trouves l'historique trop foutoir avec les merges, bin tu applatis tout dans le master en squashant. La question fondamentale derrière ça est: est-ce que les commits dans une branche ont une valeur ?

        • [^] # Re: Pourquoi ?

          Posté par . Évalué à 4.

          La question fondamentale derrière ça est: est-ce que les commits dans une branche ont une valeur ?

          Plus que oui!!! Si tu dois retrouver un bug ou relire le code, je préfère largement lire plusieurs petits commits ayant du sens plutôt qu'un seul gros commit où ton cerveau aura beaucoup (trop?) d'informations à traiter…

          Pour moi le squash n'est à utiliser que pour fusionner 2 commits qui n'auraient du être qu'un seul car tu as oublié de commiter un fichier, tu t'es aperçu que tu n'avais pas fini de résoudre le problème,…. pas pour squasher toute une branche!

          PS: tu peux remplacer %C(yellow)%d%Creset par %C(auto)%d%Creset, tu auras quelque chose de plus lisible (les références aurons une couleur différente en fonction de leur type)

          • [^] # Re: Pourquoi ?

            Posté par . Évalué à 6.

            Si tu dois retrouver un bug ou relire le code, je préfère largement lire plusieurs petits commits ayant du sens plutôt qu'un seul gros commit où ton cerveau aura beaucoup (trop?) d'informations à traiter…

            C'était une vraie question qui n'attend pas de réponse unique. Elle doit être adaptée au contexte et est forcément subjective. De ce choix découle la façon de travailler tes commits et ton historique.

            Tu as énormément de gros projets qui travaillent avec des historiques très plats car le point d'intégration est un patch (ou patch set) qui est soumis, relu et accepté. C'est par exemple le fonctionnement des projets Apache.

            A l'inverse la plupart des projets préfèrent merger 50 commits même non travaillés. C'est aussi ce que tu vois le plus en entreprise.

            Chacun a ses propriétés.

            • [^] # Re: Pourquoi ?

              Posté par . Évalué à 2.

              C'est marrant mais pour moi tes 2 exemples sont 2 extrêmes qui ne sont pas bons et qui montrent que beaucoup de gens n'ont pas compris la valeur de l'historique.

              Pour moi, la meilleure façon de faire est entre les deux, même si je suis d'accord avec toi que cela est à adapter en fonction du contexte…

              Tu as énormément de gros projets qui travaillent avec des historiques très plats car le point d'intégration est un patch (ou patch set) qui est soumis, relu et accepté. C'est par exemple le fonctionnement des projets Apache.

              Je vois 2 raisons qui pourrait pousser à faire ce choix:
              - ils n'ont pas encore adapté leur workflow avec ce que permettent de faire maintenant les DVCS (ou n'en utilisent pas encore)
              - comme c'est un projet opensource et qu'il est plus difficile "d'imposer" une façon de faire, ils préfèrent tout squasher pour éviter d'avoir à faire du babysitting en demandant systématiquement aux contributeurs de le faire (mais ils se privent d'avoir une feature construite par étape…)

              A l'inverse la plupart des projets préfèrent merger 50 commits même non travaillés. C'est aussi ce que tu vois le plus en entreprise.

              Je pense que cela est dû soit à une mauvaise maitrise de git soit au fait qu'ils ont décidé de faire du travail de porc et de se foutre de l'historique (ils en ont pas encore compris la valeur)

              En tout cas, dans les 2 cas, leur historique n'est surement pas optimal…

              • [^] # Re: Pourquoi ?

                Posté par . Évalué à 3.

                Je pense que cela est dû soit à une mauvaise maitrise de git soit au fait qu'ils ont décidé de faire du travail de porc et de se foutre de l'historique (ils en ont pas encore compris la valeur)

                Ou peut-être qu'une fois qu'on a publié sur une branche de feature/prototype/… partagée il est compliqué de réécrire l'historique et le repusher sans péter la conf de autres.
                Après on peut toujours rappeler les bonnes pratiques avant de publier, mais une fois que le mal est fait, il faut attendre que la branche soit fermée avant de procéder au nettoyage.
                On préfère alors utiliser le revert, on corrige un truc qui a fait péter la CI, …

                Cela a t'il réellement un intérêt de suivre l'historique au niveau du patchset lorsque le gestionnaire de ticket fait correctement son boulot au niveau du ticket et reférence les commits asssociés.

                Bref il faut appréhender la gestion de version et des demandes de changements comme un ensemble et ne pas se limiter à l'une ou l'autre.
                Un historique avec des commits sans référence à des tickets ne sert pas à grand chose et vice versa

                • [^] # Re: Pourquoi ?

                  Posté par . Évalué à 4.

                  Ou peut-être qu'une fois qu'on a publié sur une branche de feature/prototype/… partagée il est compliqué de réécrire l'historique et le repusher sans péter la conf de autres.

                  Avec un peu d'organisation, cela peut être fait. Au moins juste avant de merger…

                  Cela a t'il réellement un intérêt de suivre l'historique au niveau du patchset lorsque le gestionnaire de ticket fait correctement son boulot au niveau du ticket et reférence les commits asssociés.

                  Je pense légèrement différemment…

                  Pour moi, ton gestionnaire de code source doit presque (et le plus possible) se suffire à lui même.

                  Déjà, git est un DVCS. Donc comment tu fais pour consulter ton gestionnaire de ticket si t'es pas forcement connecté?
                  En plus, cela peut rendre fortement dépendant d'un gestionnaire de ticket si t'as pas bien prévu ton truc (il faut pouvoir exporter les données, le réimporter avec les même identifiants,…). Rien que si tu as fait un projet sur github, c'est pas évident.

                  Alors que si tu fais des beaux messages de commits (avec potentiellement des bouts de texte pris du gestionnaire de ticket), déjà tu risques de gagner du temps en évitant d'avoir à ouvrir un autre outils dans la majorité des cas, en plus tu es un peu plus indépendant (l'indispensable est dans le contrôleur de code source),…

                  Là où je bosse, on utilise quasiment jamais le gestionnaire de ticket lors de la recherche d'un bug dans l'historique. Si le message de commit est pas assez clair, soit on lit le code source. Si on va dans le gestionnaire de ticket, c'est qu'on a merdé notre historique.

                  • [^] # Re: Pourquoi ?

                    Posté par . Évalué à 6. Dernière modification le 03/12/14 à 18:09.

                    Tu ouvres un débat intéressant.
                    Pour le fait de se suffire à lui-même tu te focalises encore une fois sur la gestion de version.
                    Un outils de gestion de version montre pourtant ses limites rapidement pour l'analyse d'impact et oblige à des stratégies de contournements.
                    Comment reporter une demande de changement proprement qui recouvre plusieurs commits ?
                    Si elle est disséminée dans plusieurs commit d'une même branche. En se basant sur l'identifiant du ticket pour cherry picker les bon commits. En isolant une fonctionnalité dans une branche au détriment de la CI, autrement …

                    Je ne vais pas parler entrer dans un débat sur les mérites techniques d'autres outils proprio (il est clos aujourd'hui) mais certains d'entre eux résolvent élégamment en embarquant la notion de changement comme des entité à part entière dans l'outil et permettent une gestion à un plus haut niveau.
                    Par exemple Perforce ou Clearcase UCM avec ses activités. L'intégration d'une activité (deliver) merge automatiquement tous les changements associés. Des contrôles sont fait pour ne pas intégrer une fonctionnalité terminée si un commit associé à une autre non complétée
                    Un bon outil de gestion de conf par opposition à un outil de gestion de version offre donc une intégration plus cohérente avec un outil de suivi des changements.

                    Encore une fois, la gestion de configuration et le suivi des demandes de changement sont si intimement liés
                    qu'n parle de solution de SCCM (Software Change and Configuration Mgt)
                    http://www.serena.com/docs/repository/solutions/GartnerMQ-SCCM-2009.pdf

                    Avec des outils open-sources qui ne sont pas prévus pour ça on se contente de referencer le ticket dans le commit etles comits associés dans le ticket.
                    Ca reste rudimentaire, peu fiable (on oublie son id de ticket on pushe et paf), pas très abouti pour la tracabilité et les reports sur d'autres branches.

                    Déjà, git est un DVCS. Donc comment tu fais pour consulter ton gestionnaire de ticket si t'es pas forcement connecté?
                    Comment dire !
                    Déjà est-ce pertinent de considérer le suivi des demandes de changements de manière distribuée.
                    Pour résoudre un ticket ou changer des tickets qui nous sont affectés ok. Pour prendre un ticket ouvert et se retrouver à faire le boulot en doublon parce qu'un autre a cloné la base de ticket en même temps et s'affecte le même ticket en loacal c'est moins pertinent.

                    Des solutions, il en existe pour le premier cas.
                    Avec Mylyn d'Eclipse, tu récupères tes tickets en local, tu bascules entre deux et tu commit avec le bon commentaire de tickets. (Sans parler de la gestion du contexte)

                    D'autres outils proposent une solution de SCCM (bug tracking + versionning) complètement distribuée et même en open-source
                    Fossil, Veracity (http://veracity-scm.com/)

                    Bref, si on met de coté les mérites techniques de Git pour automatiser certaines tâches et sa souplesse, face à ses challenger et la hormis hype portée par Linus, Git est loin d'être la meilleure option.

                    Ca y'est le débat Git vs (Hg|Bzr|???) est réouvert. Nostalgie !
                    Mais les moutons de Panurge que nous sommes doivent suivre. La guerre est terminée. Git sort vainqueur … pour le meilleur et pour le pire.

                    • [^] # Re: Pourquoi ?

                      Posté par (page perso) . Évalué à 2.

                      Ca y'est le débat Git vs (Hg|Bzr|???) est réouvert. Nostalgie !

                      Euh je vois pas en quoi. Ton message (fort intéressant au demeurant) mentionne tout le long des outils qui vont au-delà du (D)VCS comme tu l'expliques mieux que moi (vu que je ne connais que très peu les fameux Clearcase, Perforce, Fossil…) et à la fin tu mets un petit taquet à git en mentionnant des saloperies moribondes comme bzr qui ne sont pas plus qu'un DVCS comme git mais en pourri ? Dommage de tout gâcher.

              • [^] # Re: Pourquoi ?

                Posté par (page perso) . Évalué à 5.

                • comme c'est un projet opensource et qu'il est plus difficile "d'imposer" une façon de faire, ils préfèrent tout squasher

                Euh, on n'a pas parlé de tout squasher, mais de travailler avec des patchs/patch sets. Ça n'implique pas du tout de squasher quoi que ce soit. Et pour le fait que ce genre de workflow soit utilisé par des gens qui ne savent pas ce que permet Git, rappelons que le développement de Git fonctionne comme ça … ;-).

  • # exemple

    Posté par . Évalué à 4.

    Je tente de suivre cet exemple de branches, mais je suis tout seule sur le projet. C'est toujours plus facile !

  • # Mon workflow

    Posté par . Évalué à 3.

    Comme chacun donne son workflow, je vais donner le miens.

    Je garde une branche master sur la quelle je ne travail pas directement. Je crée des branches pour tout et n'importe quoi. Quand je veux les fusionner je commence par faire un rebase -i pour nettoyer mon historique. Généralement je me retrouve avec 1 ou 2 commits et avec des messages de commits plus long, c'est l'occasion d'expliquer ce que j'ai fais. Ensuite je merge dans master.

    Je suis loin d'être un spécialiste de git. Je sais que gitflow reproduit à peu près ce shemas là, mais j'ai pas pris le temps de regarder comment il fonctionne et ça ne représente pas un gros problème de faire ça à la main (au moins le temps de comprendre bien ce que l'on fait et pourquoi).

    Ça doit permettre d'avoir un historique plus lisible et plus facile à annuler avec moins de commits.

    Avec ça j'essaie d'augmenter un peu ma fréquence de commit pour avoir une meilleure granularité lorsque je fais mon rebase interactif et je ne m'embête pas fréquemment pour le message de commit parce que je sais que je reviendrais dessus quoi qu'il arrive.

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Mon workflow

      Posté par . Évalué à 1.

      C'est à peu prêt le "github flow", qui est parfait pour du dev web (ou des scripts de maintenance,…) où tu ne maintient qu'une version mais c'est plus problématique pour maintenir plusieurs version d'un logiciel…

      • [^] # Re: Mon workflow

        Posté par . Évalué à 3.

        Pourquoi ?

        J'ai eu à le faire et je n'ai pas eu de problème particulier.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Mon workflow

          Posté par . Évalué à 1.

          parce que ça deviens très vite le bordel dès que tu as plusieurs "master" à maintenir (plusieurs versions en même temps). Tu te retrouve avec des branches de hotfix, des backports entre tes branches etc…

          • [^] # Re: Mon workflow

            Posté par . Évalué à 3.

            J'avais probablement pas une pression folle avec N versions à maintenir (N tendant vers infini) et M correction à backporter (M tendant vers infini). Dans la plupart des cas, il s'agit de faire du cherrypick des commits de master et de gérer les conflits/évolutions (en commençant toujours dans une branche séparée).

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Mon workflow

          Posté par . Évalué à 3.

          Pourquoi ?

          euuhhh….
          Si tu n'as qu'un master avec des "feature branch" et que tu veuilles faire de la maintenance sur une version "extraite" d'un ancien commit de master, à ce moment là, je suppose que tu as au moins créé une branche de bug fixe pour maintenir cette version.

          Donc soit :

          • tu me parles d'un projet d'application où vous ne supporter que la dernière version (et les bugs fixes sont dans la version d'après et c'est bien un workflow se rapprochant du "githubflow"). Ce qui arrive souvent quand on fait un petit projet opensource…
          • tu as créé une branche de maintenance et tu ne l'as pas indiqué dans ton message (auquel cas tu te rapproches plus du workflow "gitflow")
          • je ne vois pas comment tu fais de la maintenance sur une vieille version sans créer de branche…
          • [^] # Re: Mon workflow

            Posté par . Évalué à 3.

            tu as créé une branche de maintenance et tu ne l'as pas indiqué dans ton message (auquel cas tu te rapproches plus du workflow "gitflow")

            Oui on crée une branche et un tag sur la branche parente.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Mon workflow

        Posté par (page perso) . Évalué à 2.

        Ça et le fait de pouvoir "valider" le code sans latence.
        C'est ce genre de truc qu'on utilise lorsqu'on bosse sur des projets web / natifs avec une bonne couverture de tests et un bonne CI. En général branches + rebase -i (vive autosquash aussi, ex https://coderwall.com/p/hh-4ea) + pull request
        Le résultat est pas mal et assez fluide, mais limité à un master.

  • # Quelques retours ...

    Posté par . Évalué à 10. Dernière modification le 03/12/14 à 11:49.

    Merci de nous faire partager votre workflow qui a le mérite d'être original (en tout cas c'est la première fois que j'en vois un de ce type) et de viser la simplicité. J'ai lu avec beaucoup d’intérêt l'article détaillé.

    Si j'essaie de résumer ton approche:

    • Une branche par fonctionnalité
    • Une branche d'intégration. Ceci est justifié par le fait que la recette est couteuse et qu'il faut regrouper les fonctionnalités par lots plutôt que de tester et déployer en continu (Continuous Delivery/Deployment http://kief.com/iterations-considered-harmful.html )
    • Une branche de prod (master) qui suit l'intégration en fast forward
    • Le dev d'une fonctionnalité n'est confié qu'à une personne: ceci permet de se réaligner par rebase plutôt que par merge une fois la branche partagé grâce au push --force. Ceci incombe au développeur
    • Pour intégrer une fonctionnalité on passe encore par un rebase avant de merger en -no--ff dans la branche d'intégration, toujours permis par le fait qu'une branche de fonctionnalité n'est sous la responsabilité que d'une seule personne à un instant donné. Pour l'intégration elle est confié à une personne qui assume le rôle d'intégrateur.

    A partir de là on peut comparer aux workflows classiques:
    - Celui de la pure Continuous Integration qui impose à toute l'équipe de travailler dans la même branche et d'intégrer au fil de l'eau chaque commit
    - Celui que sembliez utiliser précédemment qui repose sur le même principe avec les branches par fonctionnalité mais qui procède par merge pour un réalignement et pour l'intégration sans jamais insérer l'étape du rebasing. Votre modèle est un aménagement de ce workflow me semble t'il
    - Les workflows des méthodes non agiles qui ajoutent des branches de lotissement (intégration, recette, maintenance) (le SemVer que tu évoques) et qui compose entre l'approche par feature ou la pure CI pour les branche de dev. (GitFlow en est un exemple type)

    Pour les diverses interrogations que tu as soulevé:

    Avec une approche par fonctionnalité, le rôle d'intégrateur n'est pas obligatoire.
    L'intégration peut-être confiée au développeur en l'obligeant à se réaligner dans la branche de feature avant de réintégrer juste après.
    C'est ce que vous faites en demandant de rebaser sa branche avant. Ceci est plutôt malin car ça vous permet de conserver un historique lisible (nettoyé à coup de rebase -i), condensé et le plus actualisé possible par rapport à l'effort d'intégration en comparaison d'une branche de feature qui partirait d'une version stable antérieure. La contrepartie est que si vous avez moins de souplesse dans la composition des fonctionnalités. Vous ne pouvez pas maintenir en parallèle plusieurs branches d'intégration qui activerait/désactiverait certaines d'entre elles. Mais ca ne semble pas un de vos besoins.

    Pour revenir sur le rôle, avec le feature branching que vous avez choisi, vous pouvez décider d'autres stratégies d'intégration. Celle que tu évoques avec un intégrateur garant de la qualité de la branche mais aussi un workflow d'approbation basé sur une revue par ses pairs.
    Encore une fois donc le rôle d'intégrateur n'est pas obligatoire.

    Le souci que vous rencontrez, je suppose, est plutôt lié au effets pervers du feature branching et de l'isolement qu'il permet.
    Si on ne se remet pas ajours régulièrement (à chaque commit dans la branche d'intégration) on se retrouve en face d'un big bang merge au moment de l'intégration … que l'on préfère déléguer à une bonne poire qu'on nommera intégrateur ou dev senior pour lui faire avaler ce travail ingrat et délicat. Le big bang merge et la découverte tardive de problèmes d'intégration voire des conflits sémantiques (http://martinfowler.com/bliki/SemanticConflict.html) sont les arguments classiques des protagonistes de la pure CI (tous les commits dans la même branche).
    Pour un bon résumé de cette question du big bang merge encore ce bon vieux Martin Fowler:
    http://martinfowler.com/bliki/FeatureBranch.html
    A l'opposé les spécialiste de la gestion de conf prêchent l'inverse et souligne que le feature flipping reporte la complexité sur l'infra de CI en faisant exploser la combinatoire:
    http://www.cmcrossroads.com/article/agile-perspective-branching-and-merging

    Enfin le feature flipping n'est tout simplement pas toujours possible.

    Vaste débat !

    Avec la CI, L'inconvénient est celui que tu évoques. Les commits de chaque fonctionnalité s'entrecroisent et la désactivation d'une fonctionnalité devient ardue si. La réponse de ces mêmes protagonistes de la CI est que le feature branching bien que facile en apparence est toujours la moins bonne solution. Il faut préférer agir au niveau du code, de l'architecture pour permettre d'activer/désactiver les features à l'exécution tout en conservant la même base de code (Une fonctionnalité est en général reportée, rarement abandonnée).
    C'est la qu'il faut toujours se poser la question de savoir si on peut pratiquer le feature flipping (décrit ici http://martinfowler.com/bliki/FeatureToggle.html ), et pour les migrations techniques le branching by abstraction (http://martinfowler.com/bliki/BranchByAbstraction.html ) avant de se lancer dans les branches à tire-larigot.

    Pour limiter cet isolement, il faut donc s'astreindre à se tenir à jour à chaque commit sur la branche d'intégration. Ca n'est pas de la pure CI (seuls les fonctionnalité prêtes sont integrée, celles en cours de dev restent isolée du flux d'intégration)
    Avec une approche par merge, ceci peut être renforcée par le serveur de CI qui va déclencher un merge automatique à chaque commit dans l'intégration vers la feature et casser le build de la feature. Atlassian Stash et Bamboo permettent de mettre en oeuvre ceci et Atlassian investit pas mal là dessus, maas des exemples avec Jenkins existent aussi (http://testonsteroid.tumblr.com/post/14231829163/continuous-integration-flow ). Ceci me parait plus compliqué voire impossible avec cette approche par rebase que tu préconises étant donné que l'historique d'une feature peut-être réécrit par le serveur de build en concurrence avec le dev. Il faut donc s'en remettre à la discipline des devs dans ce cas.

    Parmi tes autres interrogations, celle de la branche de maintenance.
    Ceci dépend vraiment de votre méthode de projet et du contrat qui vous lie à vos clients.
    Vous pouvez incorporer à la fois des corrections de bugs et des nouvelles fonctionnalité à chaque livraison si le client est prêt à l'accepter en terme de délais, que vous êtes "sûr" de la qualité de votre code. Dans ce cas vous ne conserver qu'une unique branche continue et vous réservez les branches courtes aux hotfixes que vous n'oublierez pas de réintegrer. C'est ce qui se pratiques avec les méthodes agiles (ce qui semble être votre cas puisque tu parles de sprint)

    Le mot de la fin:
    Le workflow universel n'existe pas. Git Flow n'est qu'un exemple et il faut d'abord poser à plat son processus de developpement avant de se plonger dans son workflow Git. On trouve à minima un workflow par approche (séquentiel, RUP/itératif, Agile/Scrum, Continuous Delivey, …) et on doit souvent le réadapter au contexte du projet.
    Les vieux dinosaures dont je suis évoqueraient ce bon vieux PGCL (Plan de Gestion de Configuration Logicielle) ou SCM Plan.

    Voilà … Merci à toi si tu as eu le courage de me lire jusqu'au bout et merci encore pour ton témoignage.

    • [^] # Re: Quelques retours ...

      Posté par . Évalué à 1. Dernière modification le 03/12/14 à 13:40.

      La réponse de ces mêmes protagonistes de la CI est que le feature branching bien que facile en apparence est toujours la moins bonne solution.

      De nature CIste, apparemment, je ne me vois pas désactivé un feature en le supprimant du master, cela me semble en effet totalement bizarre comme pratique.

      Du coup je me demande, est ce très répandu comme pratique ? Pourquoi préférer cela à des décennies de normalisation de la conception logiciel ?

      • [^] # Re: Quelques retours ...

        Posté par (page perso) . Évalué à 2.

        je ne me vois pas désactivé un feature en le supprimant du master, cela me semble en effet totalement bizarre comme pratique.

        Pourquoi ?
        Ça veut dire que tu conserves du code mort, non ?
        Il existe des domaines où c'est juste interdit d'avoir du code mort (pour pouvoir certifier dans l'aviation par exemple).

        des décennies de normalisation de la conception logiciel ?

        cad ? (juste pour être certains de comprendre ce qu'il y a derrière ;-))

    • [^] # Re: Quelques retours ...

      Posté par (page perso) . Évalué à 4.

      Merci pour tous ces détails ;-)

      Pour en rajouter sur le Branch by abstraction il y a aussi toute une série d'articles de qualité chez Paul Hammant : http://paulhammant.com/categories.html#branch_by_abstraction,_etc

      Et d'ailleurs ça amène la discussion (on rejoint aussi ce que dit ckyl plus haut en parlant d'historique très plat) sur le terrain du Trunk based development.

      Pour certains projets c'est réellement ce que je tenterais. Par exemple pour des projets web aujourd'hui je ferais plutôt du TBD (qui peut se faire aussi avec des branches à très courte durée de vie + merge squash) avec du Feature flipping (ou Branch by Abstraction). Au final je trouve que ça offre une réponse intéressante à des questions complexes comme "comment effectuer des changements profond sans diverger trop longtemps" (ex changement du moteur de stockage d'une appli). Mais pour que ce soit intéressant il faut CI avec déploiement continu. C'est un workflow vraiment agréable.

      Dans l'idéal j'appliquerais ça à notre projet. Mais c'est juste pas possible. Pour avoir un déploiement continu il faut une procédure automatique qui permet de valider un code. Et donc un ensemble de tests avec une couverture et une fiabilité suffisante pour garantir le bon fonctionnement. Déjà là on tombe dans un cas tordu, si je développe une appli web ou un drone la fiabilité suffisante n'a pas le même sens. Et là je suis dans le deuxième. Ensuite la couverture aujourd'hui n'est pas suffisante car on s'interface avec du matériel. Et aujourd'hui nous n'avons pas d'autres solutions que de valider manuellement le comportement en réel (nous avons déjà eu des cas où tout fonctionnait bien en simulation mais pas en croisé sur des problématiques de temps réel par exemple).
      Donc on introduit de la latence et on ne peut valider automatiquement.

      Il pourrait à la rigueur y avoir des variations.
      Par exemple un fonctionnement style pull request avec validation de toutes les branches indépendamment. Dans ce cas une fois de retour de tests, il suffit d'accepter ce qui passe. Mais (il y a toujours un mais) on ne teste jamais l'intégration de multiples développements (l'intérêt de notre branche d'intégration) et les tests deviennent encore plus fastidieux.

      • [^] # Re: Quelques retours ...

        Posté par . Évalué à 2.

        Mais pour que ce soit intéressant il faut CI avec déploiement continu.

        Pas forcément du continuous deployment. Ceci implique que tout changement est déployé en automatique et dans la vraie vie c'est rarement le cas (sauf che les equipes de GitHub ;-)
        En moins ambitieux on peut se limiter au continuous delivery. On s'assure que tout changement est potentiellement déployable.
        Le déploiement effectif en recette se fait sur une base régulière qui regroupe plusieurs changement mais ne fait fait qu'officialiser la livraison. Par exemple si pratiques vraiment le continuous delivery avec Scrum, une livraison à la fin d'un sprint est presqu'un n on événement. C'est juste un tag à poser sur un commit pour satisfaire le time boxing mais rien de plus. Ca fournit des indicateurs sur la gestion de projet mais c'est tout.
        Les équipes Scrum qui se limitent à la CI sont souvent confrontée à un mini effet tunnel ou on essaie de stabiliser la branche à l'arrache
        avant la fin du sprint alors que le continuous delivery sécurise ça tout au long du sprint.
        C'est vraiment bien expliquer dans ce post:
        http://kief.com/iterations-considered-harmful.html

        Dans ton cas, effectivement même le continuous delivery n'est pas possible car vous n'avez pas l'assurance de la qualité.

        La question est donc de savoir comment gérer au mieux l'activation sélective des fonctionnalités.
        Soit par le code (feature flipping ?), soit avec des branches d'intégration.
        Si vous vous limitez à une seule branche d'intégration et que vous avez besoin d'ôter une feature, tu peux reprendre à partir du dernier commit avant la feature à desactiver et rebaser les autres features par dessus. Ton approche avec des rebases+merge pour l'intégration fonctionnera très bien. (plus propre que revert amha)
        Si vous souhaitez avoir plusieurs configurations de features activées différents et donc plusieurs branches d'intégration, vous devez procéder par merge pour l'intégration (sans rebase) d'une feature et ne jamais vous réaligner dedans. (merge d'une branche d'intégration vers la feature branch proscrit)
        Une branche de feature reste donc isolée complètement et une fois complétée, elle est intégrée par merge dans une ou plusieurs branches

        Avantage plus de souplesse pour switcher des fonctionnalités.
        Inconvénient: Merge d'intégration plus conséquents (git rerere à gogo)

        Sinon avec votre workflow actuel, dans mon post initial, je ne voyais pas avec ton approche par rebase comment obliger le dev à rebaser à chaque commit sur l'intégration, mais un "simple" hook pre-commit qui empêche le push de la feature branch si la base n'est pas le denier commit d'integ suffit.

        • [^] # Re: Quelques retours ...

          Posté par . Évalué à 2.

          Et pour reprendre un peu ce que dit Gama plus bas.

          Si vous maintenez plusieurs branches d'intégration, y'a peut-être des trucs à faire pour automatiser des merges.
          Genre:
          un fichier de description qui liste les features actives de la branches (avec A,B,C int1=A,B, int2 = A,C, …)
          Merge automatique sur chaque branche lors d'un commit dans une feature.
          Si ca passe nickel, sinon on corrige dans la feature branch et pas la branch d'int (pour détecter les pbs d'intégration avec d'autres features au plus tôt dans philosophie CI)

          En plus vous avez un changelog gratos ….

          Une belle usine à gaz ;-)
          J'ai pas le temps de te faire un schéma hélas.

  • # Patch

    Posté par (page perso) . Évalué à 3.

    J'ai pas de solution toute faite mais une piste.

    J'ai l'impression en fait que tu cherches une approche par patches.

    Chaque "features" branches est en fait un patch disponible pour intégration/test.
    Certes il existe plusieurs petit commits pour une seul branche mais le diff (head - master) représente un seul et unique patch que vous voulez appliquer ou pas.

    Dans ce context, j'aurai plutôt une approche de ce type :

    • Une branche master qui ne contient que les fonctionnalité validé (et donc finale).
    • Une branche par fonctionnalité. Au maximum basé sur la tête le master HEAD. Éventuellement, si une fonctionnalité B dépend d'une fonctionnalité A, alors branche_B est basée sur branche_A au lieu de master.
    • Un magnifique script que je vous laisse développer qui merge le tout (les fonctionnalité que tu veux tester) dans une branche d'intégration "temporaire"
      • si tes tests fonctionnels sont bon, master fast-forward sur la branche d'intégration. Les branches de fonctionnalités non mergées sont rebasé sur le nouveau HEAD
      • si tes tests fonctionnels passent pas, tu vires la branches (ou tu la laisse de coté pour l'historique) et tu créés une nouvelle branche d'intégration pour les tests suivants.

    C'est globalement la même chose que ce que tu as, mais sans branche d'intégration "réelle". Les branches d'intégrations sont temporaires et privées (pour les tests).

    Le fait que tes merges de features soient automatisés doit améliorer le git-rerere. En effet, si tu changes juste un truc dans une branche, tu repars toujours du même point et tu applique tout tes patchs/merge dans le même ordre. Donc tu te retrouves à corriger que les conflits ajoutés par le changement que tu viens de faire.

    Tu simplifies aussi le boulot d'intégrateur puisque qu'il se retrouve à "seulement" décider quels patches tester, lancer le script et corriger les nouveaux conflit.

    Au niveau outils, il y a stgit qui gèrent une séries de patch par dessus une tête de branche. Chaque patch est versionné dans une branche git séparée. Je sais pas si ça correspond à ton besoin car stgit construit lui-même les branches en fonctions des fichiers .patch que tu lui donne à manger, mais ça peut donner des idées.

    Matthieu Gautier|irc:starmad

  • # Complexe

    Posté par . Évalué à 3.

    J4ai toujours trouver la gestion de version très complexe. Surtout si on s'amuse avec plusieurs version à maintenir, et des branches à rebase/merger souvent.

    C'est rare d'avoir des merges sans conflit. J'ai du mal à comprendre fondamentalement pourquoi c'est si compliqué.

    "La première sécurité est la liberté"

    • [^] # Re: Complexe

      Posté par . Évalué à 2.

      C'est rare d'avoir des merges sans conflit. J'ai du mal à comprendre fondamentalement pourquoi c'est si compliqué.

      C'est un remarque sans véritable base statistique…..
      Soit tu travailles pas sur les mêmes périmètres fonctionnels et à ce moment là, c'est plutôt l'inverse, y'a (presque) jamais de merge!
      Par contre si tu travailles les mêmes périmètres fonctionnels et donc sur les même fichiers de codes, ben c'est un peu normal que tu ais des problèmes de merge, non?!?

      • [^] # Re: Complexe

        Posté par . Évalué à 2.

        oui sans doute.

        Je bosse sur un projet un peu spécial. 10 repositories (CVS, SVN et GIT), un gros repository git que l'on rebase pour rester sur le projet original mais avec quelques modif à nous. A plusieurs dessus, c'est pas marrant (l'historique change).

        "La première sécurité est la liberté"

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.