J'ai souvent lu que Hg (et Git et ...) sont meilleurs pour fusionner que SVN mais je n'ai jamais vu d'exemples pratiques où Hg/Git peut fusionner quelque chose où SVN échoue (ou où SVN a besoin d'une intervention manuelle). Pourriez-vous publier quelques listes pas à pas des opérations de branchement/modification/validation /...- qui montrent où SVN échouerait pendant que Hg/Git se déplace avec bonheur? Des cas pratiques, pas très exceptionnels s'il vous plaît ...
Quelques informations: nous avons quelques dizaines de développeurs travaillant sur des projets utilisant SVN, avec chaque projet (ou groupe de projets similaires) dans son propre référentiel. Nous savons comment appliquer les branches des versions et des fonctionnalités afin de ne pas rencontrer de problèmes très souvent (c'est-à-dire que nous y sommes allés, mais nous avons appris à surmonter problèmes de Joel de "un programmeur" causant un traumatisme à toute l'équipe "ou" ayant besoin de six développeurs pendant deux semaines pour réintégrer une branche "). Nous avons des branches de publication qui sont très stables et ne sont utilisées que pour appliquer des corrections de bugs. Nous avons des troncs qui devraient être suffisamment stables pour pouvoir créer une version en une semaine. Et nous avons des branches de fonctionnalités sur lesquelles des développeurs uniques ou des groupes de développeurs peuvent travailler. Oui, ils sont supprimés après la réintégration afin de ne pas encombrer le référentiel. ;)
J'essaie donc toujours de trouver les avantages de Hg/Git sur SVN. J'adorerais avoir une expérience pratique, mais il n'y a pas encore de plus gros projets que nous pourrions passer à Hg/Git, donc je suis coincé avec jouer avec de petits projets artificiels qui ne contiennent que quelques fichiers constitués. Et je cherche quelques cas où vous pouvez ressentir la puissance impressionnante du Hg/Git, car jusqu'à présent, j'ai souvent lu à leur sujet mais je ne les ai pas trouvés moi-même.
Je n'utilise pas Subversion moi-même, mais d'après les notes de publication de Subversion 1.5: suivi des fusionnements (fondamentaux) il semble qu'il y ait les différences suivantes par rapport au fonctionnement complet du suivi des fusions - DAG systèmes de contrôle de version comme Git ou Mercurial.
La fusion de tronc en branche est différente de la fusion de branche en tronc: pour une raison quelconque, la fusion de tronc en branche nécessite --reintegrate
option pour svn merge
.
Dans les systèmes de contrôle de version distribués comme Git ou Mercurial, il n'y a pas de différence technique entre le tronc et la branche: toutes les branches sont créées égales (il peut y avoir social différence, cependant ). La fusion dans les deux sens se fait de la même manière.
Vous devez fournir un nouveau -g
(--use-merge-history
) option pour svn log
et svn blame
pour prendre en compte le suivi des fusions.
Dans Git et Mercurial, le suivi des fusions est automatiquement pris en compte lors de l'affichage de l'historique (journal) et du blâme. Dans Git, vous ne pouvez demander à suivre le premier parent qu'avec --first-parent
(Je suppose qu'une option similaire existe également pour Mercurial) pour "supprimer" les informations de suivi de fusion dans git log
.
D'après ce que je comprends svn:mergeinfo
La propriété stocke des informations par chemin sur les conflits (Subversion est basée sur les changements), tandis que dans Git et Mercurial, il s'agit simplement de valider des objets qui peuvent avoir plusieurs parents.
"Problèmes connus" sous-section pour le suivi de fusion dans Subversion suggère que la fusion répétée/cyclique/réfléchissante pourrait ne pas fonctionner correctement. Cela signifie qu'avec les historiques suivants, la deuxième fusion peut ne pas faire la bonne chose ('A' peut être un tronc ou une branche, et 'B' peut être une branche ou un tronc, respectivement):
* --- * --- x --- * --- y --- * --- * --- * --- M2 <- A \\/ - * ---- M1 --- * --- * ---/<- B
Dans le cas où l'art ASCII ci-dessus est cassé: la branche 'B' est créée (bifurquée) à partir de la branche 'A' à la révision 'x', puis la branche 'A' plus tard est fusionnée à la révision 'y' dans la branche 'B' comme fusionnez 'M1', et enfin la branche 'B' est fusionnée dans la branche 'A' en tant que fusion 'M2'.
* --- * --- x --- * ----- M1 - * --- * --- M2 <- A \// \- * --- y --- * --- * ---/<- B
Dans le cas où l'art ASCII ci-dessus est cassé: la branche 'B' est créée (bifurquée) à partir de la branche 'A' à la révision 'x', elle est fusionnée dans la branche 'A' à 'y' en tant que 'M1', et plus tard fusionné à nouveau dans la branche "A" comme "M2".
Subversion peut ne pas prendre en charge le cas avancé de fusion croisée .
* --- b ----- B1 - M1 - * --- M3 \\// \X / \/\ / \- B2 - M2 - *
Git gère très bien cette situation dans la pratique en utilisant une stratégie de fusion "récursive". Je ne suis pas sûr de Mercurial.
Dans "Problèmes connus" il y a un avertissement indiquant que le suivi des fusions peut ne pas fonctionner avec les renommages de fichiers, par exemple quand un côté renomme le fichier (et peut-être le modifie), et le second modifie le fichier sans renommer (sous l'ancien nom).
Git et Mercurial gèrent très bien un tel cas dans la pratique: Git en utilisant détection de renommage , Mercurial en utilisant suivi du renommage .
HTH
Moi aussi, je cherchais un cas où, disons, Subversion ne parvient pas à fusionner une branche et Mercurial (et Git, Bazaar, ...) fait la bonne chose.
Le livre SVN décrit comment les fichiers renommés sont fusionnés de manière incorrecte . Cela s'applique à Subversion 1.5 , 1.6 , 1.7 et 1.8 ! J'ai essayé de recréer la situation ci-dessous:
cd /tmp[.____. E5Erm -rf svn-repo svn-checkout svnadmin create svn-repo svn checkout file: /// tmp/svn-repo svn -checkout cd svn-checkout mkdir trunk branches echo 'Goodbye, World!' > trunk/hello.txt svn add trunk branches svn commit -m 'Initial import.' svn copy '^/trunk' '^/branches/rename' -m 'Créer une branche.' Commutateur svn '^/trunk'. Echo 'Hello, World!' > hello.txt svn commit -m 'Update on trunk.' svn switch '^/branches/rename'. svn rename hello.txt hello.en.txt svn commit -m 'Renommer sur la branche.' commutateur svn '^/trunk'. svn merge --reintegrate '^/branches/rename'
Selon le livre, la fusion devrait se terminer proprement, mais avec des données incorrectes dans le fichier renommé car la mise à jour sur trunk
est oubliée. Au lieu de cela, j'obtiens un conflit d'arborescence (c'est avec Subversion 1.6.17, la dernière version de Debian au moment de la rédaction):
--- Fusion des différences entre les URL de référentiel dans '.': Un hello.en.txt C hello.txt Résumé des conflits: Conflits d'arbre: 1
Il ne devrait pas y avoir de conflit du tout - la mise à jour doit être fusionnée dans le nouveau nom du fichier. Alors que Subversion échoue, Mercurial gère cela correctement:
rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo 'Goodbye, World!' > hello.txt
hg add hello.txt
hg commit -m 'Initial import.'
echo 'Hello, World!' > hello.txt
hg commit -m 'Update.'
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m 'Rename.'
hg merge
Avant la fusion, le référentiel ressemble à ceci (de hg glog
):
@ changeset: 2: 6502899164cc | tag: astuce | parent: 0: d08bcebadd9e | utilisateur: Martin Geisler | date: jeu 01 avr 12:29:19 2010 +0200 | résumé: Renommer. | | o changeset: 1: 9d06fa155634 |/utilisateur: Martin Geisler | date: jeu 01 avr 12:29:18 2010 +0200 | résumé: mise à jour. | o changeset: 0: d08bcebadd9e utilisateur: Martin Geisler date: jeu 01 avr 12:29:18 2010 +0200 résumé: importation initiale.
La sortie de la fusion est:
fusion de hello.en.txt et hello.txt en hello.en.txt 0 fichier mis à jour, 1 fichier fusionné, 0 fichier supprimé, 0 fichier non résolu (fusion de branche, n'oubliez pas de vous engager)
En d'autres termes: Mercurial a pris le changement de la révision 1 et l'a fusionné dans le nouveau nom de fichier de la révision 2 (hello.en.txt
). Le traitement de ce cas est bien sûr essentiel pour prendre en charge le refactoring et le refactoring est exactement le genre de chose que vous voudrez faire sur une branche.
Sans parler des avantages habituels (commits offline, processus de publication , ...) voici un exemple de "fusion" I comme:
Le scénario principal que je continue de voir est une branche sur laquelle ... deux des tâches non liées sont réellement développées
(il est parti d'une fonctionnalité, mais il a conduit au développement de cette autre fonctionnalité.
Ou il est parti d'un correctif, mais il a conduit au développement d'une autre fonctionnalité).
Comment fusionner une seule des deux fonctionnalités sur la branche principale?
Ou Comment isolez-vous les deux entités dans leurs propres branches?
Vous pouvez essayer de générer une sorte de correctifs, le problème est que vous n'êtes plus sûr des dépendances fonctionnelles qui aurait pu exister entre:
Git (et Mercurial aussi je suppose) proposent l'option rebase --onto pour rebaser (réinitialiser la racine de la branche) une partie d'une branche:
- x - x - x (v2) - x - x - x (v2.1)
\
x - x - x (v2-only) - x - x - x (wss)
vous pouvez démêler cette situation où vous avez des correctifs pour la v2 ainsi qu'une nouvelle fonctionnalité wss en:
- x - x - x (v2) - x - x - x (v2.1)
|\
| x - x - x (v2-only)
\
x - x - x (wss)
, vous permettant de:
L'autre fonctionnalité que j'aime (dont l'influence fusionne) est la possibilité de squash commits (dans une branche pas encore poussée vers une autre repo) afin de présenter:
Cela garantit des fusions beaucoup plus faciles, avec moins de conflits.
Nous avons récemment migré de SVN vers GIT et avons été confrontés à cette même incertitude. Il y avait beaucoup de preuves anecdotiques que GIT était meilleur, mais il était difficile de trouver des exemples.
Je peux vous dire cependant que GIT est BEAUCOUP MIEUX à la fusion que SVN. C'est évidemment anecdotique, mais il y a un tableau à suivre.
Voici quelques-unes des choses que nous avons trouvées:
Lorsque nous évaluions GIT, nous avons effectué les tests suivants. Ceux-ci montrent GIT comme le gagnant en matière de fusion, mais pas autant. En pratique, la différence est beaucoup plus grande, mais je suppose que nous n'avons pas réussi à reproduire les situations que SVN gère mal.
D'autres en ont couvert les aspects les plus théoriques. Je peux peut-être apporter une perspective plus pratique.
Je travaille actuellement pour une entreprise qui utilise SVN dans un modèle de développement "branche de fonctionnalité". C'est:
En général, cela fonctionne. SVN peut être utilisé pour un flux comme celui-ci, mais ce n'est pas parfait. Il y a certains aspects du SVN qui gênent et façonnent le comportement humain. Cela lui donne des aspects négatifs.
^/trunk
. Ces portées fusionnent les enregistrements d'informations dans l'arbre et finissent par interrompre le suivi de la fusion. De faux conflits commencent à apparaître et la confusion règne.svn merge
fait ce que vous voulez. La fusion de vos modifications nécessite (nous dit-on) --reintegrate
sur la commande de fusion. Je n'ai jamais vraiment compris ce commutateur, mais cela signifie que la branche ne peut plus être fusionnée dans le tronc. Cela signifie que c'est une branche morte et vous devez en créer une nouvelle pour continuer à travailler. (Voir la note)Ce qui a tendance à se produire, c'est qu'un ingénieur crée une branche le premier jour. Il commence son travail et l'oublie. Quelque temps plus tard, un patron arrive et lui demande s'il peut libérer son travail dans le coffre. L'ingénieur redoute ce jour car la réintégration signifie:
... et parce que l'ingénieur fait cela le moins possible, il ne se souvient pas de l '"incantation magique" pour effectuer chaque étape. De mauvais commutateurs et URL se produisent, et soudain, ils sont en désordre et ils vont chercher "l'expert".
Finalement, tout se calme et les gens apprennent à gérer les lacunes, mais chaque nouveau démarreur rencontre les mêmes problèmes. La réalité éventuelle (par opposition à ce que j'ai exposé au début) est:
...mais...
Heureusement, l'équipe est assez petite pour faire face, mais elle ne s'agrandira pas. Le fait est que rien de tout cela n'est un problème avec CVCS, mais plus que parce que les fusions ne sont pas aussi importantes qu'en DVCS, elles ne sont pas aussi lisses. Cette "fusion de friction" provoque un comportement qui signifie qu'un modèle "Feature Branch" commence à se décomposer. Les bonnes fusions doivent être une caractéristique de tous les VCS, pas seulement des DVCS.
Selon this il y a maintenant un --record-only
commutateur qui pourrait être utilisé pour résoudre le --reintegrate
problème, et apparemment v1.8 choisit quand faire une réintégration automatiquement, et cela n'entraîne pas la mort de la branche par la suite
Avant Subversion 1.5 (si je ne me trompe pas), Subversion avait un inconvénient important en ce qu'elle ne se souviendrait pas de l'historique des fusions.
Regardons le cas décrit par VonC:
- x - x - x (v2) - x - x - x (v2.1)
|\
| x - A - x (v2-only)
\
x - B - x (wss)
Notez les révisions A et B. Supposons que vous ayez fusionné les modifications de la révision A sur la branche "wss" à la branche "v2 uniquement" à la révision B (pour une raison quelconque), mais que vous avez continué à utiliser les deux branches. Si vous essayez de fusionner à nouveau les deux branches à l'aide de Mercurial, il ne fusionnera les modifications qu'après les révisions A et B. Avec Subversion, vous devrez tout fusionner, comme si vous n'aviez pas effectué de fusion auparavant.
Ceci est un exemple de ma propre expérience, où la fusion de B à A a pris plusieurs heures en raison du volume de code: cela aurait été une vraie douleur à traverser à nouveau , ce qui aurait été le cas avec Subversion pré-1.5.
Une autre différence probablement plus pertinente dans le comportement de fusion de Hginit: Subversion Re-education :
Imaginez que vous et moi travaillions sur du code, et que nous branchions ce code, et nous partions chacun dans nos espaces de travail séparés et apportions beaucoup, beaucoup de changements à ce code séparément, donc ils ont divergé un peu.
Quand nous devons fusionner, Subversion essaie de regarder les deux révisions — mon code modifié et votre code modifié — et il essaie de deviner comment les briser ensemble dans un gros gâchis impie. Il échoue généralement, produisant des pages et des pages de "conflits de fusion" qui ne sont pas vraiment des conflits, simplement des endroits où Subversion n'a pas réussi à comprendre ce que nous avons fait.
En revanche, alors que nous travaillions séparément dans Mercurial, Mercurial était occupé à conserver une série de changements. Et donc, quand nous voulons fusionner notre code ensemble, Mercurial a en fait beaucoup plus d'informations: il sait ce que chacun de nous a changé et peut réappliquer ces changements, plutôt que de simplement regarder le produit final et essayer de deviner comment le mettre ensemble.
Bref, la façon dont Mercurial analyse les différences est (était?) Supérieure à celle de Subversion.