Mercurial 2.1 : Les phases

Posté par  . Édité par claudex, B16F4RV4RD1N, Benoît Sibaud, NeoX, baud123 et Bruno Michel. Modéré par NeoX. Licence CC By‑SA.
Étiquettes :
39
18
fév.
2012
Gestion de versions

Mercurial, un puissant logiciel de gestion de versions écrit en Python, sort en version 2.1 après 3 mois de développement.
Pour rappel, Mercurial est un logiciel de gestion de version décentralisé, à savoir qu'il permet de travailler en équipe sans avoir besoin d'un serveur centralisé. Tous les développeurs pouvant se synchroniser entre eux, via deux méthodes :

  • pull : réception des modifications d'un dépôt distant.
  • push : envoi des modifications vers un dépôt distant.

Cette souplesse permet de hiérarchiser le développement d'un projet de mille et une façons. Cette souplesse possède un revers, quand l'on doit se synchroniser avec un dépôt distant on a souvent des conflits. Pour les résoudre, les développeurs font appel à deux méthodes :

  • merge : fusion des modifications de deux branches en une seule, les conflits sont résolus par divers outils manuels ou automatiques.
  • rebase, mq, histedit : modification de l'historique des modifications afin de le linéariser ou de le simplifier.

Dans le second cas, modifier son historique peut s'avérer bien plus désastreux que le problème initial ou l'on avait qu'un simple conflit. Git, le principal concurrent de Mercurial qui possède des fonctionnalités et des performances sensiblement identiques, est particulièrement sensible à des erreurs de rebase, car cette fonctionnalité est activé par défaut.

Pour éviter les erreurs de modifications d'historiques, Mercurial 2.1 introduit une nouvelle fonctionnalité : les phases.

Les phases sont un moyen de marquer automatiquement une suite de modifications comme étant :

  • publiques : par défaut dès que l'on fait un push/pull, les modifications envoyées/reçues deviennent publiques. Les modifications publiques ne peuvent pas voir leur historique modifié sans utiliser explicitement la nouvelle commande phase ;
  • des brouillons : tant que les modifications que l'on a créées localement n'ont pas été envoyées via un push, elle gardent le statut de brouillon, leur permettant de voir leur historique modifié ;
  • secrètes : dès que l'on utilise l'extension mq, les modifications que l'on fait avec sont marquées comme secrètes, et ne peuvent pas être envoyées via un push vers un autre dépôt. Prévenant ainsi des erreurs basiques.

Les phases permettent donc de se souvenir de ce qui a été publié et d'éviter de modifier l'historique associé. Cependant, si l'on souhaite forcer la modification d'une publication, cela est toujours possible en forçant des changements de phases, afin de pouvoir modifier l'historique.
Ainsi par défaut, Mercurial vous empêchera désormais de faire des bêtises quand vous souhaitez modifier l'historique de vos modifications.
Ce nouveau concept arrivera-t-il bientôt dans Git ?

Aller plus loin

  • # Versionnage de fichier de conf privé possible ?

    Posté par  . Évalué à 4.

    Est-ce que cette fonctionnalité permet de versionner un fichier de configuration privé ?

    Mettons que j'ai dans un projet un fichier de configuration (on va l'appeler foo.conf) que je veux gérer avec mercurial. Actuellement, j'ajoute le foo.conf par défaut au dépot. C'est la configuration exposé à l'utilisateur quand il installe le projet. Je l'adapte à mon environnement et je fais attention à ne jamais le commiter.

    • Le fichier .hgignore sert uniquement à ignorer l'ajout de fichier au dépot.
    • Pour faire attention lors d'un commit, je dois explicitement utiliser tout les fichiers à commiter $ hg ci a b c (chiant)
    • Je peux utiliser l'option -X (exclude pattern) : $ hg ci -X '*.conf' (chiant)

    Du coup c'est toujours chiant, et foo.conf n'est pas versionné. Notez que le fichier de configuration peut contenir des mots de passe, ce genre de choses.

    Avec ces phases, quelle serait la suite de commandes pour versionner foo.conf sans jamais le pusher ?

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  . Évalué à 4.

      Réponse un peu provoquante : puppet ou cfengine ?

      En fait je trouve qu'a priori, c'est une mauvaise pratique d'avoir dans le dépôt un fichier de conf avec des données non "universelles" (mots de passe, adresse IP etc), aussi ça me semblerait plus robuste d'avoir un second dépôt pour de telles données ; mais tu dois sans doute avoir une bonne raison de vouloir procéder ainsi.

      Avec des dépôts imbriqués (http://mercurial.selenic.com/wiki/NestedRepositories un jour ?, sinon il y a git submodules), tu aurais sans doute simplement ce que tu veux.

      • [^] # Re: Versionnage de fichier de conf privé possible ?

        Posté par  (site web personnel) . Évalué à 4.

        Le suivi d'un dépôt par un autre dépôt existe dans mercurial depuis 2 ans. Mais sous un autre nom: http://mercurial.selenic.com/wiki/Subrepository

        • [^] # Re: Versionnage de fichier de conf privé possible ?

          Posté par  (site web personnel) . Évalué à 5.

          Et surbrepository c'est ultra chiant, car si tu fais un push sur un des répos de ton subrépo, mais en dehors de ton subrépo ... ben tu viens de perdre le lien.
          Donc c'est bien uniquement si tu fais les pull/push depuis ton répo principal contenant les sub répos.

          Bon, je sais pas si c'est bien clair.
          DIsons que j'ai un répo Main où j'ai mis en subrépo les projets A et B.
          Si je commit dans A et B et fait les push / pull depuis main, c'est ok.
          Si je fais un clone de A, en dehors de main, puis fait un push. Ben mon main ne sera jamais à jour.
          Ca perd presque tout l'intérêt quand même...

          • [^] # Re: Versionnage de fichier de conf privé possible ?

            Posté par  (site web personnel) . Évalué à 1.

            Ce n'est toujours pas très claire. Je n'arrive pas à comprendre le problème que tu tente de soulever.

            • [^] # Re: Versionnage de fichier de conf privé possible ?

              Posté par  (site web personnel) . Évalué à 2.

              • je crée un répo main
              • je "référence" deux sous répos A et B
              • commit, push, tout va bien
              • quelqu'un récupère main

              • commit, push, pull, tout va bien

              • je clone A tout seul, en dehors de main

              • commit

              • push

              • je clone main ou je pull

              • je n'ai pas les changements effectués dans A

              Mon super répo est mort.
              Pourquoi ? Parce que la révision à fetcher depuis chaque sous répo est dans un fichier .hgsubstate.
              Donc lorsqu'on fait un push depuis main, ça met à jour ce fichier. Et lorsqu'on pull / clone on récupère cet état, donc tout va bien.
              Mais si un commit est fait entre temps, c'est mort.
              Et c'est super chiant de le mettre à jour.

              Moi ce que je voudrais c'est un moyen de dire : ici c'est la branche plop du répo A Et si on commit puis pull main, ben on a la bonne révision.

              Et franchement, pour ça subrepos c'est pas vraiment utilisable.
              Et si en plus on utilise des outils comme eclipse qui ne gèrent pas les subrépos (et donc chaque projet va pusher tout seul dans son coin) ben ça ne sert plus à rien.

              Après, j'ai peut-être loupé un truc...

              • [^] # Re: Versionnage de fichier de conf privé possible ?

                Posté par  . Évalué à 2.

                Si j'ai bien compris ton sub-repo a bien les changements mais si tu ne fais pas d'update tu ne travailleras pas dessus, c'est valable avec n'importe quel push, subrepo ou pas.

                • [^] # Re: Versionnage de fichier de conf privé possible ?

                  Posté par  (site web personnel) . Évalué à 2.

                  # initialise des répos A et B
                  $ hg init A && cd A && echo "A" > A.txt && hg ci -A -m "First commit in A" && cd ..
                  $ hg init B && cd B && echo "B" > B.txt && hg ci -A -m "First commit in B" && cd ..
                  # un main, qui va contenir mes subrépos
                  $ hg init main && cd main
                  # j'ajoute A
                  $ echo A = ../A > .hgsub
                  $ hg add .hgsub
                  $ hg clone ../A A
                  $ hg ci -m "Add A as a sub repo"
                  $ cat .hgsubstate
                  b48cea01eeae6bbb484e6062f8acc6b4924edfa6 A
                  # j'ajoute B
                  $ echo B = ../B >> .hgsub
                  $ hg clone ../B B
                  $ hg ci -m "Add B as a sub repo"
                  $ cat .hgsubstate
                  b48cea01eeae6bbb484e6062f8acc6b4924edfa6 A
                  de8150c86700e327cc1d4c9819b420b8fbe686dd B
                  # on clone le tout (histoire de faire comme si c'était distant)
                  $ cd ..
                  $ hg clone A cloneA
                  $ hg clone B cloneB
                  $ hg clone main cloneMain
                  # je vais faire un commit dans le clone de A dans mon clone de main
                  $ cd cloneMain
                  $ echo plop >> A/A.txt
                  $ hg st -S
                  M A/A.txt
                  $ cd A && hg ci -m "commit A"
                  $ cd ..
                  # on pousse, tout est ok, on va bien pousser la modif dans A (le vrai, distant)
                  $ hg push
                  pushing to ../main
                  pushing subrepo A to ../A
                  searching for changes
                  adding changesets
                  adding manifests
                  addinf file changes
                  added 1 changesets with 1 changes to 1 files
                  pushing subrepo B to ../B
                  searching for changes
                  no changes found
                  searching for changes
                  no changes found
                  # je vais dans mon clone de A, en dehors de main, et j'ai bien les modifs
                  $ cd ../cloneA
                  $ hg pull
                  pulling from ../A
                  searching for changes
                  adding changesets
                  adding manifests
                  adding file changes
                  added 1 changesets with 1 changes to 1 files
                  (run 'hg update' to get a working copy)
                  $ hg up
                  1 files updates, 0 files merged, 0 files removed, 0 files unresolved
                  # je fais un commit dans mon clone de A et je pousse
                  $ echo plop >> A.txt
                  $ hg ci -m "Commit from cloneA"
                  $ hg push
                  pushing to ../A
                  searching for changes
                  adding changests
                  adding manifests
                  adding file changes
                  added 1 changesets with 1 changes to 1 files
                  # je retourne dans mon clone de main : je n'ai aucune modification (normal il n'a pas été commité)
                  $ cd ../cloneMain
                  $ hg pull
                  pulling from ../main
                  searching for changes
                  no changes found
                  # si je vais dans A j'ai bien accès à ma modif
                  $ cd A
                  $ hg pull
                  pulling from ../A
                  searching for changes
                  adding changesets
                  adding manifests
                  adding file changes
                  added 1 changesets with 1 changes to 1 files
                  (run 'hg update' to get a working copy)
                  $ hg up
                  1 files updates, 0 files merged, 0 files removed, 0 files unresolved
                  # en retournant dans le clone main, on voit qu'il y a eu des changements.
                  # d'ailleurs on voit le changement alors que le fichier est ok (le status
                  # est donc par rapport à l'état de .hgsubstate)
                  $ cd ..
                  $ hg st -S
                  M A/A.txt
                  # un commit va me permettre de dire que mon main doit prendre en compte cette
                  # nouvelle version de A
                  $ hg ci -m "Update after pull A"
                  committing subrepository A
                  # je push, tout est bien commité
                  $ hg push
                  pushing to ../main
                  pushing subrepo A to ../A
                  searching for changes
                  no changes found
                  pushing subrepo B to ../B
                  searching for changes
                  no changes found
                  searching for changes
                  adding changesets
                  adding manifests
                  adding file changes
                  adding 1 changesets with 1 changes to 1 files
                  # si je reprend un clone de main, j'aurais bien mes modifs
                  $ cd ..
                  $ hg clone main main2
                  $ cd main2
                  $ cat A/A.txt
                  A
                  plop
                  plop
                  
                  

                  Voici donc ce qui me gène :

                  • le pull n'est pas récursif et c'est lourd. Ok, on peut utiliser onsub mais ça ne me plait guère et ça ne règle pas tout
                  • tant que je ne suis pas allé dans un clone de main, mis à jour chaque sous répo et commité l'ensemble, quiconque récupérera un clone de main ne sera pas à jour sur A

                  Ce que je voudrais c'est pouvoir dire dans mon main : tu track A/default dans A. Et donc lorsque je fais un pull tu pull A, de sorte que ce soit toujours à jour.

                  Après, peut-être que c'est une mauvaise idée, j'en sais rien. Peut-être que c'est un mauvais usage de l'outil, c'est possible aussi. Mais en l'état c'est quand même un peu lourd malheureusement.

                  • [^] # Re: Versionnage de fichier de conf privé possible ?

                    Posté par  (site web personnel) . Évalué à 1.

                    Oui, il reste de truc un peu pénible dans les subrepos. Ça s'améliore petit à petit mais on est encore loin de la perfection :-/

                  • [^] # Re: Versionnage de fichier de conf privé possible ?

                    Posté par  . Évalué à -1.

                    Je n'ai toujours pas compris, tu pourrais essayer de développer encore un peu plus ? :D

                    Je rigole, je rigole. Ça m'a fait sourire de voir la montée en puissance de tes explications.

                    Je ne connaissais pas les SubRepository. J'ai déjà utilisé son équivalent avec SVN (les externals), et c'est très bien foutu. On peut spécifier dans Main (pour reprendre les noms que t'as utilisés) qu'on souhaite qu'il "checkoute" le projet A et aussi le projet B, avec la possibilité de spécifier un numéro de révision. Si rien n'est spécifié il prend le HEAD, ce qui n'est pas forcément une bonne chose quand on veut pouvoir reproduire une version livrée à un client seulement avec le numéro de révision de Main.

                    • [^] # Re: Versionnage de fichier de conf privé possible ?

                      Posté par  (site web personnel) . Évalué à 3.

                      Ça m'a fait sourire de voir la montée en puissance de tes explications.

                      En général lorsqu'il s'agit de taper un peu sur mercurial je lâche pas trop l'affaire...
                      Non que je déteste mercurial, mais c'est souvent un second choix (git trop compliqué alors on prend mercurial, mercurial plus proche de svn, windows donc pas git donc mercurial, etc).
                      Mercurial a de bons points, il est tout de même relativement agréable à utiliser.
                      Mais il pêche toujours sur certains points, entre autre les subrepos et, surtout, la gestion des branches (et là je préfère, et de loin, git).

                      Maintenant, je l'utilise tous les jours, sans trop de problèmes particuliers.

                      il prend le HEAD, ce qui n'est pas forcément une bonne chose quand on veut pouvoir reproduire une version livrée à un client seulement avec le numéro de révision de Main.

                      Oué mais là c'est un autre problème : on ne livre pas un client avec juste un numéro de version de Main, on livre un client avec une version taguée, ainsi que ces modules. Et comme on a doit à du maven en plus (beurk) ben le tag du projet de base doit permettre de remonter l'ensemble avec les versions mavens (aucun snapshot dans la release).

                      • [^] # Re: Versionnage de fichier de conf privé possible ?

                        Posté par  . Évalué à 0.

                        Oui oui, je sais bien qu'on ne peut pas fournir un numéro de version, et je comprends parfaitement que c'est propre aux systèmes de contrôle de version décentralisés. Mais dire que, avec SVN, c'est bien pratique de pouvoir intégrer le numéro de révision de ton dépôt au logiciel.

                        À mon ancien boulot (beurk) j'utilisais Visual Studio (beurk beurk) et j'intégrais le numéro de révision directement dans une classe C# (beurk beurk beurk) grâce aux scripts de compilation, et c'était vraiment très pratique. Je ne sais si tu sais comment fonctionnent les externals sous SVN, mais si c'est bien configuré, alors la révision du projet principal permet de recréer la version du client. C'est tellement pratique qu'on ne faisait même plus de tags.

                  • [^] # Re: Versionnage de fichier de conf privé possible ?

                    Posté par  . Évalué à 2.

                    Un pull n'est pas récursif car ça n'est pas son but, tout comme un pull ne fait pas un update d'office. Je trouve normal que ça ne soit pas par défaut. Après que ce soit possible par une extension ou une option, pourquoi pas.
                    Personnellement, quand je veux systématiquement la dernière version d'un sous-projet (une lib), j'utilise un lien. Quand je veux avoir une version bien particulière j'utilise un subrepos.

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  (site web personnel) . Évalué à 1. Dernière modification le 18 février 2012 à 15:11.

      Je comprend pas pourquoi tu ajoutes le fichier au dépot si c'est de toutes façons pour ne jamais le comitter. Pour gérer les fichiers de conf privés, je vois deux façons de faire utilisables :

      • Tu n'ajoutes pas le fichier au dépot (mais éventuellement tu le cites dans le .hgignore pour qu'il ne soit pas ajouté par erreur et pour qu'il n'apparaisse pas dans la commande status). Inconvénient : le fichier n'est pas versionné ;
      • Tu crées un patch mq pour contenir le fichier. Grâce aux phases, tu ne risques pas d'envoyer le fichier par erreur lors d'un push / pull / clone. Inconvénient : il faut se souvenir d'utiliser qpush et qpop (ou rebase) quand on se promène dans l'historique (y compris lors d'un pull), sinon le fichier disparait du répertoire de travail.

      PS : en ce qui concerne l'option -X, tu peux toujours l'ajouter au hgrc du dépôt pour ne pas avoir besoin de penser à la spécifier à chaque commande...

      • [^] # Re: Versionnage de fichier de conf privé possible ?

        Posté par  . Évalué à 2. Dernière modification le 18 février 2012 à 16:14.

        Je comprend pas pourquoi tu ajoutes le fichier au dépot si c'est de toutes façons pour ne jamais le comitter.

        Pour avoir un fichier de configuration par défaut qui fonctionne pour le projet. Ça peut être un makefile générique ou un entête C. Ce fichier peut-être versionné. J'ai aussi plusieurs petit script où des mots de passe sont dans les sources.

        PS : en ce qui concerne l'option -X, tu peux toujours l'ajouter au hgrc du dépôt pour ne pas avoir besoin de penser à la spécifier à chaque commande...

        Ha, c'est chouette ça. Merci.

        • [^] # Re: Versionnage de fichier de conf privé possible ?

          Posté par  . Évalué à 1.

          Je pense que le mieux est de se débrouiller pour avoir deux fichiers dont un "d'exemple".
          Celui d'exemple peut être versionné, pas l'autre et tant pis.

        • [^] # Re: Versionnage de fichier de conf privé possible ?

          Posté par  (site web personnel) . Évalué à 3.

          Je comprend pas pourquoi tu ajoutes le fichier au dépot si c'est de toutes façons pour ne jamais le comitter.

          Pour avoir un fichier de configuration par défaut qui fonctionne pour le projet. Ça peut être un makefile générique ou un entête C. Ce fichier peut-être versionné. J'ai aussi plusieurs petit script où des mots de passe sont dans les sources.

          Ce que je fais dans ce cas là, c'est d'avoir le fichier « foo.conf » versionné qui contient les valeurs par défaut et un fichier « foo-local.conf » ignoré qui contient les valeurs de remplacement locales. Ensuite les outils (scripts de compilation, applications, etc.) lisent les deux fichiers en donnant la priorité aux valeurs provenant de « foo-local.conf ».

          Pour les scripts, ils peuvent vérifier la présence du fichier local et le sourcer le cas échéant.

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  . Évalué à 1.

      Je vais décrire la tambouille que j'utilise pour le versionnage des fichiers locaux. Certains points ont déjà été traités par d'autres commentateurs, désolé pour les doublons.

      Dans le dépôt central, les fichiers de configuration sont nommés différemment, par exemple tralala.conf.dist ou config-dist.php. Dans certains cas, il vaut mieux garder l'extension du fichier au cas où il contiendrait des infos sensibles.

      Dans les dépôts de travail, ces fichiers sont copiés en tralalaconf ou config.php. Ils sont alors versionnés dans une branche locale :

      hg branch modifslocales
      # ... crée les fichiers de conf
      hg addremove
      hg ci -m "config locale"
      
      

      Ensuite, si des modifs sont effectuées sur le dépôt central, je rebase ma branche locale sur le "tip":

      hg pull -u
      hg rebase -d tip           # d'autres syntaxes sont possibles
      
      

      Le cas le plus complexe est quand j'ai modifié une de ces instances locales, mais que je veux l'enregistrer dans le dépôt central. Autrement dit, j'ai fait une modif dans une branche, mais je veux la commiter dans la branche "default". Dans ce cas, j'utilise l'extension "shelve" :

      hg shelve -a               # met en réserve le diff et rétablit les fichiers d'origine
      hg up default              # passe dans la branche centrale
      hg unshelve                # applique le diff mis de côté
      hg ci -m "modif globale"   # commite
      hg push                    # propage
      hg up modifslocales        # retourne dans la branche locale
      hg rebase -d tip           # synchronise la branche locale avec le dernier changement
      
      

      Si quelqu'un a une solution plus simple, avec git ou hg, cela m'intéresse.

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  . Évalué à 4.

      Tu peux utiliser pour cela l'extension mq :

      hg qinit -m "un fichier de conf local" conf.patch //crée un un nouveau patch au dessus de ton dépôt
      [...créer et éditer ton fichier de conf...]
      hg qrefresh // met à jour ton patch avec tes modifications
      hg qpop // enlève ton patch mais le garde au chaud
      [continuer à travailler sur ton dépôt normalement]
      hg qpush // rappelle ton patch
      [modifie tes fichiers de patch]
      hg qrefresh
      hg qpop //ré-enlève ton patch
      
      
    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  (site web personnel) . Évalué à 1. Dernière modification le 19 février 2012 à 01:25.

      En général on préconise plutôt:

      • Soit d'avoir deux fichier (un d'exemple suivit par le DVCS et un autre non suivit)
      • Soit d'utiliser l'extension keyword pour configurer des valeurs spécifiques

      --

      Pour répondre strictement à ta question, tu peux maintenant committer ton fichier de configuration dans un changeset "secret" qui ne sera ni pusher ni puller.

      Une suite de command simple pour faire ça serait

      hg add mon-fichier.conf
      hg commit --config phases.new-commit=secret
      
      

      Par contre il faudra que tu t'arrange pour que les commit que tu veux pousser ne soit les descendants de ton changeset secret. Sinon il ne seront pas pousser non plus. Tu peux le faire soit en utilisant hg update "not secret()" pour revenir sur le parent de ton commit secret avant de commiter toi en utilisant hg rebase après coup.

      • [^] # Re: Versionnage de fichier de conf privé possible ?

        Posté par  . Évalué à 5.

        C'est pour ça que j'ai proposé d'utiliser mq. car les patch son toujours rebasés au dessus de tes changesets.

        Pour info, mq est une killer feature pour pouvoir gérer plusieurs targets sans avoir à gérer et rebaser N branches différentes quand tu souhaites maintenir des backports.
        C'est vraiment le meilleur gestionnaire de patch que j'ai pu voir. Je dirais qu'il m'est devenu indispensable. L'association des changesets secret aux patchs permet d'éviter de faire de malencontreuses erreurs. Un vrai bonheur.

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  . Évalué à 2.

      Pour les fichiers de config, le plus simple que j'ai trouvé est de versionner un template. Le fichier de config, qui lui est privé, de mon point de vue il n'a tout simplement pas a être versionné.

    • [^] # Re: Versionnage de fichier de conf privé possible ?

      Posté par  . Évalué à 2.

      Pour Git, j'ai le même problème et j'ai trouvé çà :

      Cacher un fichier modifié:
      git update-index --assume-unchanged

      Reafficher les modifs:
      git update-index --no-assume-unchanged
      Source:
      http://gitready.com/intermediate/2009/02/18/temporarily-ignoring-files.html

  • # M'étonnerait que ça arrive dans git.

    Posté par  . Évalué à 2.

    Modifier l'historique des commits est fortement déconseillé dans git. Le faire en local avec rebase est acceptable, mais le faire pour des commits publics ne me semble pas en ligne avec la "philosophie" de git. Je vois les aspects pratiques pour les conflits... mais git essaie de prévenir ces conflits le plus en amont possible. On va voir.

    En tous cas entre ça et fossil, ça bouge !

    • [^] # Re: M'étonnerait que ça arrive dans git.

      Posté par  (site web personnel) . Évalué à 4.

      Bin justement, le principe c'est d'empêcher de modifier l'historique des commits publics. Aujourd'hui avec Git il y a un risque de les modifier accidentellement, d'autant plus que l'usage de rebase est beaucoup plus courant avec Git que Mercurial. Les phases permettent d'empêcher de modifier l'historique public involontairement en gardant une trace de quels commits sont publics (donc figés) et lesquels sont locaux (donc modifiables).

      • [^] # Re: M'étonnerait que ça arrive dans git.

        Posté par  (site web personnel) . Évalué à 5.

        C'est déjà le cas dans git.

        Si tu fais une branche "Feature" dans ton dépôt local, tu la pousses sur ton dépôt public, tu rebase (obligatoirement dans ton dépôt local). Si tu la pousse à nouveau sur le dépôt public, git va râler. Et il va falloir utiliser du "--force" pour que ça marche (et tu pourras pas dire que c'était involontaire)

        Après, t'as toujours le cas où ton dépôt local est aussi le dépôt public. Mais ça reste un cas particulier (qui est déconseillé et jamais utilisé que je n'ai jamais vu)

        Matthieu Gautier|irc:starmad

        • [^] # Re: M'étonnerait que ça arrive dans git.

          Posté par  . Évalué à 1.

          Là où le système est perfectible, c'est que git ne te prévient qu'au moment du push. Résultat, tu te retrouves avec un dépôt local en vrac qui ne peut plus faire de push/pull mais qui contient tout de même de nouveau commits. On peut s'en sortir avec un nouveau clone et un peu de cueillette de cerises, mais se serait plus simple si git nous prévenait au moment du rebase.

          Au passage, je ne suis pas sûr que git râle tout le temps quand on veut propager un historique modifié, par exemple si on a fait un rebase interactif ou un de ces trucs d'édition poussée de l'historique. J'ai le souvenir du contraire, mais peut-être que ma mémoire me joue des tours. Ou alors git a amélioré cela ces derniers temps.

          • [^] # Re: M'étonnerait que ça arrive dans git.

            Posté par  . Évalué à 4.

            En vrac faut pas exagérer, suffit de re-puller origin/master dans une nouvelle branche locale temporaire et de rebaser master dessus et zou.

          • [^] # Re: M'étonnerait que ça arrive dans git.

            Posté par  (site web personnel) . Évalué à 5.

            Ce n'est pas vraiment possible (ni souhaitable d'ailleurs, de mon point de vue) de prévenir avant le push, puisque par définition les branches que tu as en local n'ont rien à voir avec les branches distantes. En particulier, rien ne m'empêche d'avoir une branche "master" locale qui ne reflète pas la branche "master" du dépôt cloné, et rien non plus ne m'empêche de vouloir publier une branche "foo" en tant que "bar". Donc git ne peut pas savoir au moment où tu réécris l'historique si c'est pour mettre à jour une branche publiée, ou pour en créer une nouvelle. On pourrait éventuellement détourner la notion de "tracking" pour les branches pour avoir une heuristique, mais franchement ça vaut probablement pas le coup.
            Bref, la notion de branche de git est assez différente de celle de mercurial.

            En tout état de cause, par défaut git t'empêche de mettre à jour une branche avec quelque chose d'autre qu'un "fast-forward", à savoir une branche qui dérive strictement de la version déjà publiée (l'historique déjà publiée est un préfixe de l'historique à publier). Cela implique de rejeter toute opération de réécriture de l'historique déjà publiée.

          • [^] # Re: M'étonnerait que ça arrive dans git.

            Posté par  (site web personnel) . Évalué à 5.

            Là où le système est perfectible, c'est que git ne te prévient qu'au moment du push. Résultat, tu te retrouves avec un dépôt local en vrac qui ne peut plus faire de push/pull mais qui contient tout de même de nouveau commits. On peut s'en sortir avec un nouveau clone et un peu de cueillette de cerises, mais se serait plus simple si git nous prévenait au moment du rebase.

            Ça se voit que tu n'utilises pas git :)

            git rebase ne change pas un historique mais en crée un autre et fait du nouveau celui de référence. C'est une nuance subtile mais elle a son importance. L'ancien historique existe toujours et est encore référencé sous le nom de "origin/feature" (origin étant le nom de ton dépôt public, ça n'a rien à voir avec le fait que ce soit l'historique d'origin)

            Il est donc possible de retrouver un historique «poussable» avec (au choix):

            • git rebase master feature --onto origin/feature # rebase les commits allant de master à feature sur origin/feature
            • git merge origin/feature

            À ce moment, feature est un commit qui descend de origin/feature. On peut donc le pousser et mettre à jour origin/feature

            Au passage, je ne suis pas sûr que git râle tout le temps quand on veut propager un historique modifié, par exemple si on a fait un rebase interactif ou un de ces trucs d'édition poussée de l'historique. J'ai le souvenir du contraire, mais peut-être que ma mémoire me joue des tours. Ou alors git a amélioré cela ces derniers temps.

            git râlera à chaque fois que tu voudras pousser une série de commits (1) sur un autre (2) alors que les nouveaux (1) ne sont pas des descendants de celui déjà présent (2). Et ce quelque soit la façon dont tu l'ais fait (rebase / rebase -i / commit --amend / filter-branch).

            Matthieu Gautier|irc:starmad

            • [^] # Re: M'étonnerait que ça arrive dans git.

              Posté par  . Évalué à 1.

              Ça se voit que tu n'utilises pas git :)

              Je te le retourne ;-). Parce que si tu rebases une branche qui a déjà était « pusher » alors oui, il a raison. Je suis d’accord que normalement, on ne fait pas dans ce cas là. Mais ça peut arriver.

              Une fois, j’ai même utilisé le rebase -i pour virer rééllement un commit qu’un client ne devait pas voir. Ça à mis un sacré bazar chez les intégrateurs qui n’avait plus de sha1 correct sur le dépôt central… C’était voulu, et on a fait passé en urgence les nouvelles réf.

              • [^] # Re: M'étonnerait que ça arrive dans git.

                Posté par  (site web personnel) . Évalué à 2.

                heu...

                Je suis d'accord avec toi (à moins que je n'ai pas compris où tu voulais en venir). Je réagissait à :

                Résultat, tu te retrouves avec un dépôt local en vrac qui ne peut plus faire de push/pull mais qui contient tout de même de nouveau commits. On peut s'en sortir avec un nouveau clone et un peu de cueillette de cerises […]

                J'expliquais juste que le dépôt n'est pas en vrac et qu'on peut s'en sortir sans mal de cranes.

                Et oui, on peut forcer un push non fast-forward. Il faut juste faire chauffer la mailling list après.

                Matthieu Gautier|irc:starmad

              • [^] # Re: M'étonnerait que ça arrive dans git.

                Posté par  . Évalué à 1.

                Le problème que tu décris vient du fait d'avoir publié (push) pas du fait qu'on ne te préviens pas que ton rebase ne sera pas pushable.
                D'ailleurs si le client avait pullé entre temps, tu peux être sûr que tu ne lui aurai jamais retiré le commit problématique sans son accord...
                Et les phases Mercurial ne feront rien contre.

                • [^] # Re: M'étonnerait que ça arrive dans git.

                  Posté par  (site web personnel) . Évalué à 1.

                  D'ailleurs si le client avait pullé entre temps, tu peux être sûr que tu ne lui aurai jamais retiré le commit problématique sans son accord...
                  Et les phases Mercurial ne feront rien contre.

                  Justement l'idée des phases Mercurial est de garder une trace des changesets qui existent à des endroits où il te sera difficile les supprimer.

          • [^] # Re: M'étonnerait que ça arrive dans git.

            Posté par  . Évalué à 2. Dernière modification le 18 février 2012 à 22:03.

            Au passage, je ne suis pas sûr que git râle tout le temps quand on veut propager
            un historique modifié, par exemple si on a fait un rebase interactif ou un de ces
            trucs d'édition poussée de l'historique. J'ai le souvenir du contraire, mais
            peut-être que ma mémoire me joue des tours. Ou alors git a amélioré cela ces
            derniers temps."

            Ta mémoire te joue des tours. Ca fait longtemps (i.e. au moins depuis que j'ai commencé à l'utiliser...) que Git interdit le non-fast-forward push par défaut. Et tous tes "trucs d'édition poussée de l'historique" (y compris un rebase interactif) sont concernés.

        • [^] # Re: M'étonnerait que ça arrive dans git.

          Posté par  (site web personnel) . Évalué à 7.

          Les versions précédentes de Mercurial se plaignait déjà au moment du push (dans la même veine de ce que fait git). La nouveauté apportée par les phases, c'est le fait de prévenir ces problèmes le plus tôt possible en refusant des le départ de récrire les partis d'historique dont on sait que leur réécriture posera problème.

          En quelque sorte c'est un équivalent du mantra: "Tu ne réécrira pas " qui prévaux dans git. Mais garanti directement par l'outil. Ça facilite la vie des gens qui ont une compréhension limité des DVCS (et la vie des gens qui les encadrent…)

          Pour finir on notera que les utilisateurs avancés pourront toujours contrôler finement la manière dont Mercurial va marquer la section de l'historique immuable. Ce n'est pas parce que les choses prévue pour être simple et sûre pour les humains normaux que les autres devrait se limiter

    • [^] # Re: M'étonnerait que ça arrive dans git.

      Posté par  (site web personnel) . Évalué à 0.

      En fait, ces extensions de mercurial sont inspirées de choses que propose git...

      • [^] # Re: M'étonnerait que ça arrive dans git.

        Posté par  . Évalué à 3.

        En fait, ces extensions de mercurial sont inspirées de choses que propose git...

        Tu peux expliquer un peu ? À ma connaissance, il n'y a pas ce genre de garde-fous dans git.

        Par exemple, il m'est déjà arrivé avec git de faire un rebase sur du code déjà propagé. Je passe sur la galère qui avait suivi.
        Et je ne suis pas le seul, puisque Linus a déjà signalé ce genre d'embrouilles dans son dépôt du noyau.

        Un détail : tu écris "ces extensions de mercurial" alors que la dépêche explique clairement que le mécanisme des phases fait partie du cœur de l'application. Ensuite, toutes les extensions doivent s'y plier, en particulier "rebase" qui est une extension livrée par défaut.

        • [^] # Re: M'étonnerait que ça arrive dans git.

          Posté par  (site web personnel) . Évalué à 1.

          Je parlais des extensions rebase, mq qui permettent de modifier l'historique.

          Ces extensions de mercurial s'inspirent de ce que propose git par défaut. Avec git, la modification d'historique est à manier avec précaution, avec mercurial, c'est impossible sans extension. Mercurial propose maintenant en plus un garde-fou. Je ne vois pas pourquoi git ne suivrait pas.

          • [^] # Re: M'étonnerait que ça arrive dans git.

            Posté par  (site web personnel) . Évalué à 4.

            L'inspiration n'est pas vraiment unidirectionnel.

            MQ est arrivé dans Mercurial avant que git-stash n'apparaissent alors que rebase est arrivé dans Mercurial après avoir introduit dans git.

            Le fait que tout ceci soit une extension ne change pas grand chose. Une des philosophie de Mercurial est d'avoir un cœur simple, facile à prendre en main et de permettre au gens de rajouter des fonctionnalités plus complexe au fur et à mesure qu'il en ressent le besoin.

      • [^] # Re: M'étonnerait que ça arrive dans git.

        Posté par  (site web personnel) . Évalué à 5.

        Pas vraiment (pour ne pas dire "pas du tout").

        Ça introduit un nouveau concept (non proposé git) pour résoudre un problème (présent dans git)

        (ta réponse sent un peut le troll du coup)

  • # rebase ou merge

    Posté par  . Évalué à 4.

    J'ai lu la discussion sur rebase et merge dans le sujet fossil, mais j'avoue ne pas bien comprendre. Actuellement pour moi la différence entre les deux actions est assez floue et il semble que mon niveau d'anglais ne me permet pas d'en comprendre d'avantage.

    Dans la pratique actuellement, j'utilise 2 branches locales dans mes projets :

    • une « master » : c'est à partir d'elle que je me synchronise avec le dépôt centrale
    • une « devel » : sur la quel je fais mes développements

    (bien sûr j'en crée d'autres mais elles sont généralement temporaires)

    Actuellement quand je veux envoyer mes modifications sur le serveur svn, je passe par une série d'étapes :

    • je nettoie ma branche devel (je commit tout ce qu'il faut et je met en « stash » ce que je ne veux pas envoyer)
    • je passe sur la branche master
    • je synchronise ma branche master avec le dépôt central (git svn rebase généralement)
    • j'effectue un merge sur ma branche master depuis ma branche devel (git merge devel)
    • je resynchronise master avec le centrale (via git svn dcommit)
    • je passe sur la branche devel
    • je merge ma branche devel avec master (git merge master)

    Au vu du mal qu'il est dis de merge j'ai l'impression que je fais un peu du n'importe quoi. Devrais-je remplacer mes merges par des rebases ?

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

    • [^] # Re: rebase ou merge

      Posté par  (site web personnel) . Évalué à 2.

      Je suis plus familier avec Mercurial que Git, mais si je comprend bien ce que tu fais, c'est la bonne méthode. Svn gère très mal les merges donc pour aller du DVCS vers Svn, il faut faire un rebase (pour éviter le merge que Svn ne gèrerait pas).

      En revanche, pour aller de Svn vers le DVCS, tu ne peux pas utiliser rebase, car rebase détruit l'historique mais comme cet historique est déjà présent dans le Svn tu risquerais d'avoir des ennuis la prochaine fois que tu synchroniserais. Note que l'impossibilité d'utiliser rebase vaut à partir du moment où les révisions sources proviennent d'un autre dépôt, que celui-ci soit un dépôt Svn ou non.

    • [^] # Re: rebase ou merge

      Posté par  . Évalué à 5.

      Au vu du mal qu'il est dis de merge j'ai l'impression que je fais un peu du n'importe quoi.

      Tu fais aussi beaucoup de boulot supplémentaire pour rien. Tes deux branches locales git pointent sur la même branche SVN t'as donc pas besoin faire tout le cirque de merge.

      # Tu pars d'un état ou master et devel sont sur le même commit
      git co devel
      do-patch-1
      git ci -a
      do-patch-2
      git ci -a
      # Tu vérifies tes commits
      git svn dcommit
      # Et maintenant tu mets à jour ton trunk
      git co master ; git svn rebase
      
      

      Si tu utilises git comme client SVN y'a pas vraiment de raison de se faire chier à remerger les deux branches git avant de pousser dans le SVN. Tu peux adopter un schema très simple: un master qui représente toujours le trunk sans jamais de modif et X branches locales pour chacunes des features sur lesquels tu bosses. Tu rebase/dcommit directement depuis ces branches et tu les détruits quand t'as fini la feature.

      Maintenant si tu tiens vraiment à le faire. Utiliser le merge au lieu du rebase te fera de toute façon perdre la trace du merge et surtout dans le SVN tu verras un seul gros commit mergé plutôt que ta suite de patch.

      Après le rebase c'est vraiment puissant quand tu commences à avoir pas mal de branches locales et que tu veux empiler tes suites de patch au cours de ton boulot et les pousser petit bout par petit bout sur les branches publiées. Par exemple tu as 20 patchs en cours sur ta branches devel, tu les cherry pick un par un sur ta branches master quand ils ont été validés et tu rebases ta branche devel pour te resynchroniser.

      Et il te reste le rebase -i pour réordonner tes commits et les modifier si besoin. L'exemple le plus con c'est quand tu relis tout avec git log -p avant de pousser et tu te rends compte que y'a un truc à modifier dans un commit ou que tu veux insérer une modif avant les commits existant pour ensuite pouvoir les simplifier.

      • [^] # Re: rebase ou merge

        Posté par  . Évalué à 2.

        Si tu utilises git comme client SVN y'a pas vraiment de raison de se faire chier à remerger les deux branches git avant de pousser dans le SVN. Tu peux adopter un schema très simple: un master qui représente toujours le trunk sans jamais de modif et X branches locales pour chacunes des features sur lesquels tu bosses. Tu rebase/dcommit directement depuis ces branches et tu les détruits quand t'as fini la feature.

        Je fais ça depuis que je me suis retrouvé en galère quand un dcommit ou un rebase vers le dépôt SVN m'a sortir des conflits que j'étais assez en peine à résoudre. Je me dis que je préfère gérer les conflits entre deux branches qu'entre mon dépôt local et le dépôt du serveur.

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

        • [^] # Re: rebase ou merge

          Posté par  . Évalué à 2.

          C'est exactement la même chose.

          • [^] # Re: rebase ou merge

            Posté par  . Évalué à 2.

            Ok, j'ai bien fait d'en parler alors :)

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

    • [^] # Re: rebase ou merge

      Posté par  . Évalué à 2.

      Sans rentrer dans aucun VCS, on peut faire un « rebase » même avec CVS.

      C’est utile pour remettre son travail à jour.
      Par ex: sur un projet tu as une branche d’intégration. Plusieurs personnes corrigent chacune un Bug dans une branche. Le premier qui termine merge sur la branche d’intégration.
      Le second doit s’assurer que sa correction fonctionne avec le code fait par le premier dévelopeur. Pour cela, il « rebase » son travail sur la livraison de son colègue. C’est comme si on repartait de la version N+1 du travail.
      Git déplace les commits, qui change d’id. Mais sur un projet sur clearcase, on mergeait la branche d’intégration sur la branche de dév. Puis si tout marche bien, on peut faire le merge sur la branche d’intégration. Ça permet de dérisquer les patchs intégrés.

      • [^] # Re: rebase ou merge

        Posté par  . Évalué à 2.

        Donc tu me conseillerais de faire plutôt ainsi ?

        • synchronisation du master avec le dépôt central
        • rebase master -> devel pour intégrer toutes les nouveautés à ma branche devel
        • dcommit depuis ma branche devel

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

        • [^] # Re: rebase ou merge

          Posté par  . Évalué à 2.

          rebase master -> devel pour intégrer toutes les nouveautés à ma branche devel

          En fait, ça dépend te ton système. Avec git, il déplace vraiment ta branche. À moins d’y avoir mis un tag ou une autre branche, les commits initiaux disparaisse. Avec d’autre ça peut-être un merge de master -> devel, et si tout marche un merge de devel(rebasé) -> master. Ça permet surtout de laisser les problèmes à régler à ceux qui veulent ajouter leur nouvelles feature ou correction. Avant de livrer. Sinon, on peut faire un merge direct, mais si ça casse quelque chose… sur un DVCS c’est pas très grave on peut revenir en arrière (git le fait, les autres je sais pas) mais sur un truc centralisé, s’il y a une boulette, il faut la corriger TRÈS vite alors qu’on veut souvent un master qui reste stable.

          Finalement, ce qui est important, c’est ton process de dev, correction, livraison et comment tu l’intègres à ton outil de gestion de conf ainsi qu’à tes éditeurs, logiciels tier de test…

      • [^] # Re: rebase ou merge

        Posté par  . Évalué à 5.

        Sans rentrer dans aucun VCS, on peut faire un « rebase » même avec CVS.

        Tellement que dans CVS rebase s'appelle update :-)

  • # SVN

    Posté par  . Évalué à 3.

    J'ai encore une question, mais celle-ci n'a pas beaucoup de rapport avec le thème de la dépêche :

    Est-il possible d'utiliser mercurial comme client subversion ? Vous auriez un lien là dessus ? Ce que je trouve quand je cherche c'est comment convertir un dépôt svn en dépôt mercurial.

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

Suivre le flux des commentaires

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