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".
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
(ou stackoverflow.com/questions/2518779/ o stackoverflow.com/questions/2544008/ ) ... etc.
0 votes
@VonC - merci. Bien que la plupart des réponses que vous avez indiquées ne correspondent pas à ce que je recherche, celle-ci semble être un exemple valable : stackoverflow.com/questions/459891/
0 votes
@ripper234 : vous voulez dire que stackoverflow.com/questions/2475831/merging-hg-git-vs-svn/ n'est pas un exemple réel et détaillé de "montre-moi déjà la fusion" ? ?? Ou stackoverflow.com/questions/2544008/ ?
0 votes
@VonC - cependant, l'exemple le plus courant est la réponse de @Greg ... maintenant j'ai juste besoin d'un exemple concret de cela. stackoverflow.com/questions/6172037/
0 votes
@VonC - J'ai mis à jour ma réponse il y a quelques minutes pour exclure les renommages/déplacements. Je sais que git les fait simplement mieux par conception (ou un défaut de conception de svn), mais je veux trouver les exemples non liés aux renommages qu'il fait également mieux ... consultez la réponse de Greg.
0 votes
Franchement, le simple fait que vous puissiez déplacer un répertoire directement depuis le système d'exploitation sans bousiller SVN parce que vous avez déplacé
.svn
autour est suffisant pour que la plupart des développeurs passent rapidement à un DVCS ;)0 votes
@VonC - git a de nombreux avantages, voir aussi stackoverflow.com/questions/871/ Je suis juste frustré par le fait que j'ai toujours entendu dire que git était meilleur pour la fusion, et que j'ai vu quelques exemples liés aux renommages, mais j'ai l'impression qu'il me manque toujours un bon exemple simple "de base" sans aucune fusion.
0 votes
@ripper234 Si stackoverflow.com/questions/459891/ est un exemple valable, pourquoi ne pas l'afficher comme la réponse à votre question et l'accepter ?
0 votes
@urig - c'est un exemple valable, mais ce n'est pas le courant dominant. Je suis en fait beaucoup plus intéressé par la réponse de @Greg à cette question, parce que c'est l'argument que j'ai toujours entendu ... nous devons juste le pirater un peu pour trouver l'exemple canonique concret une fois pour toutes.
0 votes
Bonne question. À mon avis, les pouvoirs de fusion de git sont généralement surestimés :) Et je viens d'être confronté à un autre comportement étrange de git, qui est incapable de faire une fusion automatique sur la même branche - voir la question .