Я могу лишь рассказать вам о небольшом эксперименте, в котором Git НЕ был лучше, чем Subversion (те же проблемы).
Мне было интересно об этом случае:
Вы начинаете с двух веток "mytest1" и "mytest2", основанных на одном и том же коммите.
У вас есть C-файл, который содержит функцию blub ().
В ветке mytest1 вы перемещаете "blub ()" в другую позицию в файле и делаете коммит.
В ветке mytest2 вы модифицируете blub () и делаете коммит.
На ветке mytest2 вы пытаетесь использовать "git merge mytest1".
Кажется, дает конфликт слияния.
Я надеялся, что Git распознает, что "blub ()" был перемещен в mytest1, и затем сможет автоматически объединить модификацию в mytest2 с ходом в mytest1. Но, по крайней мере, когда я пытался это не сработало автоматически ...
Поэтому, хотя я полностью понимаю, что Git намного лучше отслеживает то, что было объединено и что еще не было объединено, мне также интересно, есть ли «чистый» случай слияния, в котором Git лучше, чем SVN ...
Теперь, так как этот вопрос долго беспокоил меня, я действительно пытался создать конкретный пример, в котором Git лучше , тогда как объединение в SVN завершается неудачей.
Я нашел один здесь https://stackoverflow.com/a/2486662/1917520,, но это включает переименование, и вопрос здесь был для случая без переименования.
Итак, вот пример SVN, который в основном пытается это:
bob +-----r3----r5---r6---+
/ / \
anna / +-r2----r4----+--+ \
/ / \ \
trunk r1-+-------------------r7-- Conflict
Идея здесь такова:
- Анна и Боб являются разработчиками со своими ветками (созданными в r2, r3).
- Анна делает некоторые модификации (r4),
- Боб делает некоторые модификации (r5).
- Боб сливает модификации от Анны в свою ветку; это дает конфликты, которые Боб исправляет, а затем фиксирует (r6).
- Модификации Анны объединяются обратно в сундук (r7).
- Боб пытается слить свою модификацию обратно в ствол, и это снова вызывает конфликт.
Вот скрипт Bash , который создает этот конфликт (с использованием SVN 1.6.17, а также 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
Последний «--dry-run» говорит вам, что будет конфликт. Если вместо этого вы сначала попытаетесь объединить реинтеграцию Анны с филиалом Боба, вы также получите конфликт; так что если вы замените последний svn merge
на
svn merge ^/trunk branches/bob
это также показывает конфликт.
Вот то же самое с 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
Содержимое файла f.txt изменяется следующим образом.
Начальная версия
A
A
B
B
Анны, модификации
A
MA
A
B
B
модификации Боба
A
MB
A
B
MB
B
После слияния Анны с веткой Боба
A
MAB
A
B
MB
B
Как уже указывало много людей: проблема в том, что Subversion не может вспомнить, что Боб уже разрешил конфликт. Поэтому, когда вы попытаетесь теперь слить ветвь Боба в ствол, вам придется заново разрешить конфликт.
Git работает совершенно по-другому. Вот некоторое графическое представление того, что делает git
bob +--s1----s3------s4---+
/ / \
anna / +-s1----s2----+--+ \
/ / \ \
master s1-+-------------------s2----s4
s1 / s2 / s3 / s4 - это снимки рабочего каталога, которые делает git.
Примечания:
- Когда Анна и Боб создают свои ветви развития, это
НЕ создавать любые коммиты под git. мерзавец просто запомнит это
обе ветви изначально ссылаются на один и тот же объект коммита
мастер ветка. (Этот коммит в свою очередь будет ссылаться на снимок s1).
- Когда Анна реализует свою модификацию, это создаст
новый снимок "s2" + объект коммита.
Объект фиксации включает в себя:
- Ссылка на снимок (s2 здесь)
- Сообщение коммита
- Информация о предках (других объектах коммита)
- Когда Боб реализует свою модификацию, это создаст еще один снимок
s3 + объект коммита
- Когда Боб объединяет модификации Анны в свою ветку разработки
это создаст еще один снимок s4 (содержащий слияние его
изменения и изменения Анны) + еще один объект коммита
- Когда Анна объединяет свои изменения с главной веткой, этобыть слияния "ускоренной перемотки" в показанном примере, потому что мастер не изменился за это время.Что означает «ускоренная перемотка вперед», так это то, что мастер просто укажет на снимок s2 из анны, ничего не сливая.При таком «перемотке вперед» не будет даже другого объекта коммита.Ветвь "master" прямо сейчас будет ссылаться на последний коммит из ветки "anna"
- Когда bob теперь объединяет свои изменения в ствол, произойдет следующее:
- git найдетвыяснилось, что коммит от anna, который создал снимок s2, является (прямым) предком коммита bobs, который создал снимок s4.
- из-за этого git снова "перемотает" ветку master до последнегоcommit ветви "bob".
- снова, это даже не создаст новый объект commit.Ветвь "master" будет просто указана на последний коммит ветки "bob".
Вот вывод "git ref-log", который показывает всеэто:
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
Как вы можете видеть из этого:
- когда мы идем в ветку разработки Анны (HEAD @ {7}), мы не перейти на другой коммит, мы сохраняем коммит;git просто помнит, что мы сейчас находимся в другой ветви
- В HEAD @ {5} мы переходим к начальной ветви bob;это переместит рабочую копию в то же состояние, что и основная ветвь, потому что bob еще ничего не изменил
- В HEAD @ {2} мы возвращаемся в главную ветвь, поэтому к тому же объекту фиксации все запущеноfrom.
- Head @ {1}, HEAD @ {0} показывают слияния "ускоренной перемотки", которые не создают новые объекты фиксации.
С "git cat-В файле HEAD @ {8} -p "вы можете просмотреть полную информацию о начальном объекте коммита.Для приведенного выше примера я получил:
tree b634f7c9c819bb524524bcada067a22d1c33737f
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200
Initial file
Строка «tree» идентифицирует снимок s1 (== b634f7c9c819bb524524bcada067a22d1c33737f), к которому относится этот коммит.
Если я сделаю «git cat-file HEAD @ {3} -p "Я получаю:
tree f8e16dfd2deb7b99e6c8c12d9fe39eda5fe677a3
parent 83db5d741678908d76dabb5fbb0100fb81484302
parent 346ce9fe2b613c8a41c47117b6f4e5a791555710
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200
anna merged into bob with conflict
Это выше показывает объект коммита, bob, созданный при слиянии ветки разработки Анны.Снова строка «дерево» относится к созданному снимку (здесь s3).Дополнительно обратите внимание на «родительские» строки.Второй, который начинается с «parent 346ce9f», позже сообщает git, когда вы пытаетесь объединить ветку разработки bob с веткой master, что этот последний коммит bob имеет последний коммит анны как предка.Вот почему git знает, что слияние ветки разработки bob с master веткой - это «ускоренная перемотка вперед».