58 votes

Vraiment, un exemple concret que la fusion dans Git est plus facile que dans SVN ?

Question sur Stack Overflow Comment et/ou pourquoi la fusion dans Git est-elle meilleure que dans SVN ? est une excellente question avec des grandes réponses . Cependant, aucun d'entre eux ne montre un exemple simple où la fusion dans Git est plus efficace que la fusion dans Git. SVN .

Au cas où cette question serait fermée en raison d'un doublon, qu'est-ce qui est le plus important ?

  1. Un scénario de fusion concret
  2. Comment est-ce difficile dans SVN ?
  3. Comment la même fusion est plus facile dans Git ?

Quelques points :

  • Pas de philosophie ou d'explications profondes sur ce qu'est un DVCS, s'il vous plaît. C'est très bien, vraiment, mais je ne veux pas que leurs détails obscurcissent la réponse à cette question (IMHO) importante.
  • Je ne me soucie pas du "SVN historique" pour le moment. Veuillez comparer Git moderne (1.7.5) à SVN moderne (1.6.15).
  • Pas de renommages s'il vous plaît - je sais que Git détecte les renommages et les déplacements, alors que SVN ne le fait pas. C'est très bien, mais je cherche quelque chose de plus profond, et un exemple qui n'implique pas de renommages ou de déplacements.
  • Pas de rebase ou d'autres opérations "avancées" de Git. Montrez-moi simplement la fusion, s'il vous plaît.

0 votes

@suravi - Je ne parle pas du tout de branchement local, ni de performance. Je veux comprendre pourquoi/si il y a moins d'interruptions de service. conflits de fusion dans git que dans svn.

0 votes

Vous voulez dire stackoverflow.com/questions/2475831/merging-hg-git-vs-svn n'a pas répondu à votre question actuelle ? (ou stackoverflow.com/q/459891/6309 ?)

0 votes

23voto

Greg Hewgill Points 356191

D'un pratique La fusion a traditionnellement été "difficile" en raison de ce que j'appelle le problème de la "fusion du big bang". Supposons qu'un développeur travaille sur du code depuis un certain temps et qu'il n'a pas encore validé son travail (peut-être le développeur est-il habitué à travailler dans Subversion contre trunk et ne commet pas de code inachevé). Quand le développeur commet finalement, il y aura beaucoup de changements tous regroupés en un seul commit. Pour les autres développeurs qui veulent fusionner leur travail avec ce "big bang" commit, l'outil VCS ne va pas avoir assez d'informations sur la façon dont le premier développeur est arrivé au point où il a commis, donc vous obtenez juste "voici un conflit géant dans cette fonction entière, allez le corriger".

D'autre part, l'habituel style de travailler avec Git et d'autres DVCS qui ont des branches locales bon marché, est de commiter régulièrement. Une fois que vous avez fait un bout de travail qui a du sens, vous le livrez. Il n'est pas nécessaire qu'il soit parfait, mais il doit constituer une unité de travail cohérente. Lorsque vous revenez pour fusionner, vous avez toujours cet historique de petits commits qui montrent comment vous êtes passé de l'état initial à l'état actuel. Lorsque le DVCS fusionne ce travail avec celui d'autres personnes, il dispose de beaucoup plus d'informations sur les modifications apportées et le moment où elles ont été effectuées. plus petit y moins de conflits.

Le fait est que vous pouvez toujours faire de la fusion un problème difficile avec Git en faisant un seul big bang commit seulement après avoir terminé quelque chose. Git vous encourage à faire de plus petits commits (en les rendant aussi indolores que possible), ce qui rend les fusions futures plus faciles.

0 votes

C'est une excellente motivation. J'ai déjà entendu ces arguments et je pense qu'ils sont corrects. Avez-vous rencontré un exemple concret qui l'illustre ?

3 votes

C'est peut-être vrai dans Darcs, mais ce n'est pas vrai dans Git, car Git pour la fusion ne considère dans le cas habituel que trois versions : la nôtre, la leur et l'ancêtre (base de fusion).

0 votes

Pour votre gouverne, si vous ou quelqu'un peut transformer ce cas d'utilisation en un exemple concret, j'accepterai sa réponse, car je pense que c'est l'exemple par excellence.

17voto

Ingo Blackman Points 433

Je peux seulement vous parler d'une petite expérience où Git n'était PAS meilleur que Subversion (mêmes problèmes).

Je m'interrogeais sur cette affaire : Vous commencez avec deux branches "mytest1" et "mytest2" toutes deux basées sur le même commit. Vous avez un fichier C qui contient une fonction blub(). Dans la branche mytest1, vous déplacez "blub()" à une position différente dans le fichier et vous faites un commit. Dans la branche mytest2, tu modifies blub() et tu commences. Sur la branche mytest2, vous essayez d'utiliser "git merge mytest1".

Cela semble donner un conflit de fusion. J'espérais que Git reconnaîtrait que "blub()" a été déplacé dans mytest1 et qu'il serait alors capable de fusionner automatiquement la modification dans mytest2 avec le déplacement dans mytest1. Mais au moins quand j'ai essayé, cela n'a pas fonctionné automatiquement...

Ainsi, même si je comprends parfaitement que Git est bien meilleur pour suivre ce qui a été fusionné et ce qui ne l'a pas encore été, je me demande également s'il existe un cas de fusion "pur" dans lequel Git est meilleur que SVN...

Comme cette question me turlupine depuis longtemps, j'ai essayé de créer un exemple concret où Git est meilleur alors que la fusion dans SVN échoue.

J'en ai trouvé un ici https://stackoverflow.com/a/2486662/1917520 La question portait sur un cas où il n'y a pas eu de changement de nom.

Voici donc un exemple de SVN qui essaie essentiellement de faire cela :

bob        +-----r3----r5---r6---+
          /                /      \
anna     /  +-r2----r4----+--+     \
        /  /                  \     \
trunk  r1-+-------------------r7-- Conflict

L'idée ici est :

  • Anna et Bob sont tous deux des développeurs avec leurs propres branches (créées dans r2,r3).
  • Anna fait quelques modifications (r4),
  • Bob fait quelques modifications (r5).
  • Bob fusionne les modifications d'Anna dans sa branche ; cela crée des conflits, que Bob corrige et commute (r6).
  • Les modifications d'Annas sont réintégrées dans le tronc (r7).
  • Bob essaie de réintégrer sa modification dans le tronc et cela donne à nouveau un conflit.

Voici un Bash script, ce qui produit ce conflit (en utilisant le SVN 1.6.17 et aussi le SVN 1.7.9) :

#!/bin/bash
cd /tmp
rm -rf rep2 wk2
svnadmin create rep2
svn co file:///tmp/rep2 wk2
cd wk2
mkdir trunk
mkdir branches
echo -e "A\nA\nB\nB" > trunk/f.txt
svn add trunk branches
svn commit -m "Initial file"
svn copy ^/trunk ^/branches/anna -m "Created branch anna"
svn copy ^/trunk ^/branches/bob  -m "Created branch bob"
svn up 
echo -e "A\nMA\nA\nB\nB" > branches/anna/f.txt
svn commit -m "anna added text"
echo -e "A\nMB\nA\nB\nMB\nB" > branches/bob/f.txt
svn commit -m "bob added text"
svn up
svn merge --accept postpone ^/branches/anna branches/bob
echo -e "A\nMAB\nA\nB\nMB\nB" > branches/bob/f.txt
svn resolved branches/bob/f.txt
svn commit -m "anna merged into bob with conflict"
svn up
svn merge --reintegrate ^/branches/anna trunk
svn commit -m "anna reintegrated into trunk"
svn up
svn merge --reintegrate --dry-run ^/branches/bob trunk

Le dernier "--dry-run" vous indique qu'il y aura un conflit. Si, au lieu de cela, vous essayez d'abord de fusionner la réintégration d'Anna dans la branche de Bob, vous obtenez également un conflit ; donc si vous remplacez le dernier svn merge avec

svn merge ^/trunk branches/bob

cela montre aussi un conflit.

Voici la même chose avec Git 1.7.9.5 :

#!/bin/bash
cd /tmp
rm -rf rep2
mkdir rep2
cd rep2
git init .
echo -e "A\nA\nB\nB" > f.txt
git add f.txt
git commit -m "Initial file"
git branch anna
git branch bob
git checkout anna
echo -e "A\nMA\nA\nB\nB" > f.txt
git commit -a -m "anna added text"
git checkout bob
echo -e "A\nMB\nA\nB\nMB\nB" > f.txt
git commit -a -m "bob added text"
git merge anna
echo -e "A\nMAB\nA\nB\nMB\nB" > f.txt
git commit -a -m "anna merged into bob with conflict"
git checkout master
git merge anna
git merge bob

Le contenu de f.txt change comme suit.

Version initiale

A
A
B
B

Les modifications apportées par Anna

A
MA
A
B
B

Modifications apportées par Bob

A
MB
A
B
MB
B

Après que la branche d'Anna soit fusionnée dans la branche de Bob

A
MAB
A
B
MB
B

Comme tant de gens l'ont déjà souligné : Le problème est, que subversion ne peut pas se rappeler que Bob a déjà résolu un conflit. Ainsi quand vous essayez maintenant de fusionner la branche de Bob dans le tronc, alors vous devez résoudre à nouveau le conflit.

Git fonctionne de manière complètement différente. Voici quelques représentations graphiques de ce que fait Git.

bob         +--s1----s3------s4---+
           /                /      \
anna      /  +-s1----s2----+--+     \
         /  /                  \     \
master  s1-+-------------------s2----s4

s1/s2/s3/s4 sont les instantanés du répertoire de travail que prend git.

Notes :

  • Lorsque anna et bob créent leurs branches de développement, cela va PAS créer des commits sous git. git va juste se souvenir que les deux branches se réfèrent initialement au même objet de commit que la branche maître. (Ce commit fera à son tour référence au snapshot s1).
  • Quand Anna mettra en œuvre sa modification, cela créera une nouvel instantané "s2" + un objet commit. Un objet commit comprend :
    • Une référence à l'instantané (s2 ici)
    • Un message d'engagement
    • Informations sur les ancêtres (autres objets d'engagement)
  • Quand bob implémentera sa modification, cela créera un autre instantané s3 + un objet commit
  • Lorsque bob fusionne les modifications d'annas dans sa branche de développement cela créera un autre instantané s4 (contenant une fusion de ses changements et des modifications d'Anna) + un autre objet commit
  • Quand anna fusionnera ses changements dans la branche master, cela aura pour effet de sera une fusion "fast-forward" dans l'exemple montré, parce que le master n'a pas n'a pas changé entre-temps. Ce que "fast-forward" signifie ici est, que le master va simplement pointer vers le snapshot s2 d'anna sans fusionner quoi que ce soit. Avec une telle "avance rapide" il n'y aura il n'y aura même pas d'autre objet commit. La branche "master" va juste se référer directement au dernier commit de la branche "anna".
  • Lorsque Bob fusionne maintenant ses changements dans le tronc, ce qui suit se produit :
    • git découvrira que le commit de anna qui a créé l'instantané s2 est un ancêtre (direct) du commit de bobs, qui a créé l'instantané s4.
    • à cause de cela, git va à nouveau "avancer rapidement" la branche master vers le dernier commit de la branche "bob".
    • encore une fois, cela ne créera même pas un nouvel objet commit. La branche "master" sera simplement dirigée vers le dernier commit de la branche "bob".

Voici la sortie de "git ref-log" qui montre tout cela :

88807ab HEAD@{0}: merge bob: Fast-forward
346ce9f HEAD@{1}: merge anna: Fast-forward
15e91e2 HEAD@{2}: checkout: moving from bob to master
88807ab HEAD@{3}: commit (merge): anna merged into bob with conflict
83db5d7 HEAD@{4}: commit: bob added text
15e91e2 HEAD@{5}: checkout: moving from anna to bob
346ce9f HEAD@{6}: commit: anna added text
15e91e2 HEAD@{7}: checkout: moving from master to anna
15e91e2 HEAD@{8}: commit (initial): Initial file

Comme vous pouvez le voir :

  • quand nous allons dans la branche de développement d'anna (HEAD@{7}) nous ne pas changement à un commit différent, nous gardons le commit ; git se souvient simplement que nous sommes maintenant sur une branche différente
  • A HEAD@{5}, nous nous déplaçons vers la branche initiale de bob ; cela va amener la copie de travail au même état que la branche maîtresse, car b de travail au même état que la branche maître, car bob n'a encore rien modifié. rien pour l'instant
  • A HEAD@{2} nous revenons à la branche master, donc au même objet commit d'où tout a commencé.
  • Head@{1},HEAD@{0} montrent les fusions "fast-forward", qui ne créent pas de nouveaux objets commit.

Avec "git cat-file HEAD@{8} -p" vous pouvez inspecter les détails complets de l'objet commit initial. Pour l'exemple ci-dessus, j'ai obtenu :

tree b634f7c9c819bb524524bcada067a22d1c33737f
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200

Initial file

La ligne "arbre" identifie le snapshot s1 (==b634f7c9c819bb524524bcada067a22d1c33737f) auquel ce commit fait référence.

Si je fais "git cat-file HEAD@{3} -p", j'obtiens :

tree f8e16dfd2deb7b99e6c8c12d9fe39eda5fe677a3
parent 83db5d741678908d76dabb5fbb0100fb81484302
parent 346ce9fe2b613c8a41c47117b6f4e5a791555710
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200

anna merged into bob with conflict

Cette image montre l'objet commit, bob créé lors de la fusion de la branche de développement d'anna. Encore une fois, la ligne "tree" fait référence au snapshot créé (ici s3). Notez également les lignes "parent". La seconde qui commence par "parent 346ce9f" indique à git, lorsque vous essayez de fusionner la branche de développement de bob dans la branche master, que ce dernier commit de bob a le dernier commit de anna comme ancêtre. C'est pourquoi git sait que la fusion de la branche de développement de bob dans la branche master est un "fast-forward".

1 votes

Attendez. Pourquoi cela démontre-t-il que Subversion est meilleur que Git pour ce flux de travail ? Je pense que le comportement de Git est préférable, puisqu'il détecte simplement que Bob a déjà résolu le conflit (en fait, il a enregistré comment résoudre le conflit), et accélère le commit. La vraie question est de savoir ce qui se passe quand Anna veut fusionner ses modifications dans le tronc, sans réaliser que Bob a déjà intégré ses modifications et d'autres encore. Git indique toujours à Anna qu'il y a un conflit, ce qui est important.

2 votes

OK, ma réponse peut prêter à confusion : La première partie fait référence à une expérience de fusion pure où git et svn sont identiques (dans une branche vous déplacez une fonction, dans une autre branche vous modifiez une fonction, lorsque vous fusionnez git et svn, les deux donnent un conflit). Le site deuxième La partie (à laquelle vous faites référence) répond en fait à la question posée : Elle donne un exemple où git est définitivement meilleur que svn. Donc oui : la deuxième partie de la réponse démontre un cas où git est meilleur.

0 votes

Ah, merci. Je dois avoir mal lu cette ligne importante. Je me suis levé tard. Juste une observation rapide. La détection des déplacements de blocs est vraiment un résultat du moteur de différenciation, et non du système de contrôle de version, en soi. Vous pouvez toujours construire un résolveur de conflits plus intelligent au-dessus de votre VCS, mais Git ne le fait pas par défaut parce qu'il essaie de rester neutre du point de vue de la langue. git blame -CCC fait cela.

6voto

Jakub Narębski Points 87537

Je n'ai pas d'exemples concrets, mais tout type de fusion répétée est difficile, en particulier pour ce que l'on appelle fusionner en croix .

   a
  / \
 b1  c1
 |\ /|
 | X |
 |/ \|
 b2  c2

<em>fusionner b2 et c2</em>


La page du wiki sur Subversion Wiki décrivant les différences entre fusionner info fusion assymétrique de Subversion (avec les directions 'sync' et 'reintegrate') et fusionner suivi de La fusion symétrique basée sur le DVCS comporte une section " Fusion symétrique avec fusion croisée "

0 votes

Cela me semble assez concret ! Cela arrive tout le temps dans les grandes équipes avec des flux de travail moins rigoureux : tout le monde bricole et, périodiquement, tire du dépôt central ou d'un autre développeur qui a codé la chose dont il a besoin.

0 votes

WTF ? b2 == c2 - la fusion n'est pas nécessaire. Mensonge détecté

0 votes

@LazyBadger : Premièrement, la fusion peut être maléfique, ou juste résoudre les conflits différemment. Deuxièmement, la ligne ---- pourrait être une série de révisions sans fusion, non montrées pour simplifier le graphique.

4voto

Spoike Points 32082

L'exemple le plus concret auquel je peux penser est la fusion la plus simple qui n'entraîne pas de conflits de fusion. Cependant (TL;DR) avec cet exemple, Git est toujours une procédure intrinsèquement plus simple qu'avec Subversion. Voyons pourquoi :

Subversion

Considérez le scénario suivant dans subversion ; le tronc et la branche de fonctionnalité :

   1  2  3
…--o--o--o              trunk
          \4  5
           o--o         branches/feature_1

Pour fusionner, vous pouvez utiliser la commande suivante dans subversion :

# thank goodness for the addition of the --reintegrate flag in SVN 1.5, eh?
svn merge --reintegrate central/repo/path/to/branches/feature_1

# build, test, and then... commit the merge
svn commit -m "Merged feature_1 into trunk!"

Dans subversion, la fusion des changements nécessite un autre commit. Il s'agit de publier dans le tronc les changements que la fusion a effectués en appliquant les changements sur le répertoire virtuel de la branche de fonctionnalités. De cette façon, tous ceux qui travaillent avec le tronc peuvent maintenant l'utiliser et le graphique de révision ressemble à ceci :

   1  2  3      6
…--o--o--o------o       /trunk
          \4  5/
           o--o         /branches/feature_1

Voyons comment cela est fait dans git.

Git

Dans Git, ce commit de fusion n'est pas vraiment nécessaire car les branches sont des signets glorifiés sur le graphe de révision. Ainsi, avec le même type de structure de graphe de révision, cela ressemble à ceci :

         v-- master, HEAD

   1  2  3
…--o--o--o
          \4  5
           o--o

              ^-- feature_branch

Avec la tête actuellement sur la branche master, nous pouvons effectuer une simple fusion avec la branche feature :

# Attempt a merge
git merge feature_branch

# build, test, and then... I am done with the merge

... et il le fera avance rapide la branche vers le commit où la branche de fonctionnalité pointe. Ceci est rendu possible parce que Git sait que le but de la fusion est un descendant direct et que la branche courante a seulement besoin de prendre en compte tous les changements qui se sont produits. Au final, le graphe de révision ressemblera à ceci :

   1  2  3  4  5
…--o--o--o--o--o
               ^-- feature_branch, master, HEAD

Les changements n'ont pas besoin d'un nouveau commit car tout ce que git a fait est de déplacer les références de la branche vers le haut. Tout ce qui reste à faire est de publier cela dans le dépôt public si vous en avez un :

# build, test, and then... just publish it
git push

Conclusion

Compte tenu de ce scénario simple, vous pouvez affirmer deux choses dans la différence entre Subversion et Git :

  • SVN requiert au moins quelques commandes pour finaliser la fusion et forces vous permet de publier votre fusion comme un commit.
  • Git ne requiert que une commande et fait ne pas forcer de publier votre fusion.

Comme il s'agit du scénario de fusion le plus simple, il est difficile d'affirmer que subversion est plus facile que git.

Dans des scénarios de fusion plus difficiles, git vous offre également la possibilité de rebase la branche qui produit un graphe de révision plus simple au prix de réécriture de l'histoire . Une fois que vous avez pris le coup de main, et que vous évitez de publier des réécritures historiques de choses déjà publiées, ce n'est pas vraiment une mauvaise chose à faire.

Bases interactives n'entre pas dans le cadre de la question mais, pour être honnête, il vous permet de réorganiser, de réduire et de supprimer des commits. Je ne voudrais pas volontairement revenir à Subversion car la réécriture de l'historique n'est pas possible par conception.

2voto

TheGreyMatter Points 27

Pour que la réponse soit brève, dans DVCS, étant donné que vous disposez d'un contrôle de source local, si quelque chose se passe mal dans le processus de fusion (ce qui arrivera probablement dans les grandes fusions), vous pouvez toujours revenir à une version locale précédente qui contient les modifications que vous avez apportées avant la fusion, puis réessayer.

Vous pouvez donc effectuer une fusion sans craindre que vos modifications locales ne soient endommagées au cours du processus.

2 votes

Ça ne répond pas vraiment à la question. Ce qui est différent dans un DVCS par rapport à SVN dans votre exemple n'est pas évident.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X