Слияние: Hg / Git против SVN

144

Я часто читаю, что Hg (и Git и ...) лучше объединяются, чем SVN, но я никогда не видел практических примеров того, как Hg / Git может слить что-то, где SVN выходит из строя (или где SVN требует ручного вмешательства). Не могли бы вы опубликовать несколько пошаговых списков операций ветвления / изменения / фиксации /...-, которые показывают, где SVN потерпит неудачу, в то время как Hg / Git счастливо движется? Практические, не исключительные случаи, пожалуйста ...

Немного предыстории: у нас есть несколько десятков разработчиков, работающих над проектами, использующими SVN, с каждым проектом (или группой похожих проектов) в своем собственном репозитории. Мы знаем, как применять ветки релизов и функций, чтобы мы не сталкивались с проблемами очень часто (то есть, мы были там, но мы научились преодолевать проблемы Джоэла «одного программиста, причиняющего травму всей команде»). или "требуется шесть разработчиков на две недели для реинтеграции филиала"). У нас есть ветки релизов, которые очень стабильны и используются только для исправления ошибок. У нас есть транки, которые должны быть достаточно стабильными, чтобы иметь возможность создавать релиз в течение одной недели. И у нас есть тематические ветви, над которыми могут работать отдельные разработчики или группы разработчиков. Да, они удаляются после реинтеграции, поэтому они не загромождают репозиторий. ;)

Поэтому я все еще пытаюсь найти преимущества Hg / Git перед SVN. Я бы хотел получить практический опыт, но пока нет более крупных проектов, которые мы могли бы перевести на Hg / Git, поэтому я застрял в игре с небольшими искусственными проектами, которые содержат только несколько готовых файлов. И я ищу несколько случаев, когда вы можете почувствовать впечатляющую силу Hg / Git, поскольку до сих пор я часто читал о них, но сам не смог их найти.

stmax
источник
2
Я думаю, что вы должны обратить внимание на точные дубликаты: stackoverflow.com/questions/43995/… stackoverflow.com/questions/459891/…
P Shved
11
Я уже прочитал первый, другой был новым. Но им уже 1-2 года, и, похоже, они в основном связаны с проблемами до svn-1.5 (где у svn еще не было отслеживания слияний).
Stmax
2
Просто комментарий, что вы также можете объединить Bazaar с git / hg в качестве еще одной DVCS, которая будет правильно обрабатывать перечисленные ниже проблемы. И так как вы упомянули попытку найти преимущества: одно простое логистическое преимущество git / hg / bzr состоит в том, что ветки не являются глобальными, как в svn. Вам не нужно видеть 67 филиалов, когда к вам обращается только пара. Каждый выполняет свою работу в «частных» ветвях, а затем использует отличную возможность слияния, чтобы слить обратно, не потея, сработает ли слияние в 99% случаев.
Wadesworld
5
@wade: вы рассматриваете "частные" филиалы как преимущество в корпоративной среде? Я беспокоюсь о резервных копиях. у меня часто есть тематические ветки, которые живут 1-2 месяца до реинтеграции ..
stmax
9
@stmax: действительная проблема. Тем не менее, во многих корпоративных средах с Subversion вы обнаруживаете, что люди откладывают регистрацию до тех пор, пока их код не станет идеальным, и у вас там будет то же самое.
Wadesworld

Ответы:

91

Сам я не использую Subversion, но из примечаний к выпуску Subversion 1.5: отслеживание слияний (основополагающее) похоже, что есть следующие отличия от того, как работает отслеживание слияний в системах контроля версий с полной DAG , таких как Git или Mercurial.

  • Слияние транка с веткой отличается от слияния ветки с транком: по некоторым причинам слияние транка с веткой требует --reintegrateопции svn merge.

    В распределенных системах контроля версий, таких как Git или Mercurial, нет никакой технической разницы между стволом и ветвью: все ветви созданы равными ( хотя может быть социальная разница). Слияние в любом направлении выполняется одинаково.

  • Вам необходимо предоставить опцию new -g( --use-merge-history) svn logи svn blameучесть отслеживание слияний.

    В Git и Mercurial отслеживание слияний автоматически учитывается при отображении истории (журнала) и вины. В Git вы можете запросить подписку на первого родителя только с помощью --first-parent(я думаю, что подобная опция существует и для Mercurial), чтобы «отбросить» информацию об отслеживании слияний в git log.

  • Из того, что я понимаю, svn:mergeinfoсвойство хранит информацию о конфликтах по каждому пути (Subversion основана на наборах изменений), тогда как в Git и Mercurial это просто коммит объектов, которые могут иметь более одного родителя.

  • Подраздел «Известные проблемы» для отслеживания слияния в Subversion предполагает, что повторное / циклическое / отражающее слияние может работать неправильно. Это означает, что в следующих историях второе слияние может не сработать («A» может быть стволом или ответвлением, а «B» может быть ответвлением или стволом соответственно):

    * --- * --- x --- * --- y --- * --- * --- * --- M2 <- A
             \ \ /
              - * ---- M1 --- * --- * --- / <- B
    

    В случае, если вышеприведенный ASCII-art нарушается: ветвь «B» создается (разветвляется) из ветки «A» при ревизии «x», затем более поздняя ветвь «A» объединяется при ревизии «y» в ветку «B» как слияние 'M1', и, наконец, ветвь 'B' объединяется с ветвью 'A' как слияние 'M2'.

    * --- * --- x --- * ----- M1 - * --- * --- M2 <- A
             \ / / 
              \ - * --- у --- * --- * --- / <- B
    

    В случае, если вышеприведенный ASCII-art нарушается: ветвь «B» создается (разветвляется) из ветки «A» при ревизии «x», она объединяется с ветвью «A» в «y» как «M1», а затем снова объединены в ветвь «A» как «M2».

  • Subversion может не поддерживать расширенный случай слияния .

    * --- ----- б B1 - M1 - * --- M3
         \ \ / /
          \ ИКС /
           \ / \ /
            \ - В2 - М2 - *
    

    Git прекрасно справляется с этой ситуацией на практике, используя «рекурсивную» стратегию слияния. Я не уверен насчет Mercurial.

  • В «Известных проблемах» есть предупреждение о том, что отслеживание слияний может не работать с переименованием файлов, например, когда одна сторона переименовывает файл (и, возможно, изменяет его), а вторая сторона изменяет файл без переименования (под старым именем).

    И Git, и Mercurial на практике справляются с таким случаем: Git использует обнаружение переименования , Mercurial использует отслеживание переименования .

НТН

Якуб Наребски
источник
каким-то образом (ошибка в анализаторе Markdown?) часть после <pre>...</pre>блока не имеет отступов, как должно быть ...
Якуб Наренбский
1
+1 за много подробных примеров. Я еще не понимаю, почему пример в первом ascii-art может вызвать проблемы. это выглядит как стандартный способ обработки ветвей объектов: предположим, что A - это ствол, B - это ветвь, состоящая из объектов. вы сливаетесь еженедельно из A в B, и когда вы закончите с функцией, вы объединяете все из B в A, а затем удаляете B., который всегда работал для меня. я неправильно понял диаграмму?
Stmax
1
Обратите внимание, что я не знаю (я не проверял), что приведенные выше примеры действительно создают проблемы в Subversion . Я думаю, что переименования и слияния - настоящая проблема в SVN.
Якуб Наребски
2
Реинтеграция слияний - это специальная опция, которая поможет вам в наиболее распространенном случае слияния - в svn нет никакой технической разницы между ветвями и транком. Я склонен никогда не использовать его и придерживаюсь стандартной опции слияния. Тем не менее, единственная проблема с svn merge заключается в том, что он рассматривает перемещение / переименование как удаление + дополнение.
gbjbaanb
--reintegrateустарела.
naught101
120

Я тоже искал случай, когда, скажем, Subversion не может слить ветку и Mercurial (а Git, Bazaar, ...) делает правильные вещи.

Книга SVN описывает, как переименованные файлы объединяются неправильно . Это относится к Subversion 1.5 , 1.6 , 1.7 и 1.8 ! Я попытался воссоздать ситуацию ниже:

CD / TMP
rm - rf svn - репо svn - оформить заказ
свнадмин создать свн - репо
svn checkout file : /// tmp / svn - repo svn - checkout
cd svn - оформить заказ
mkdir стволовые ветви
эхо 'До свидания, мир!' > багажник / привет . текст 
SVN добавить ствол ветви
svn commit - m 'Первоначальный импорт.' 
svn copy '^ / trunk' '^ / branch / rename' - m 'Создать ветку.' 
SVN-переключатель «^ / trunk» . 
эхо "Привет, мир!" > Привет . текст    
svn commit - m 'Обновление транка.' 
SVN-переключатель '^ / Branches / Rename' . 
SVN переименовать привет . TXT привет . en . текст 
svn commit - m 'Переименовать на ветке.' 
SVN-переключатель «^ / trunk» . 
SVN Merge - реинтегрировать '^ / Branches / Rename' 

Согласно книге, слияние должно завершиться чисто, но с неверными данными в переименованном файле, так как обновление на trunkзабыто. Вместо этого я получаю конфликт дерева (это с Subversion 1.6.17, новейшей версией Debian на момент написания):

--- Объединение различий между URL-адресами хранилища в '.':
Привет.en.txt
   C hello.txt
Резюме конфликтов:
  Дерево конфликтов: 1

Никакого конфликта не должно быть - обновление должно быть объединено с новым именем файла. В то время как Subversion терпит неудачу, Mercurial обрабатывает это правильно:

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

Перед слиянием репозиторий выглядит так (с hg glog):

@ changeset: 2: 6502899164cc
| тег: совет
| родитель: 0: d08bcebadd9e
| пользователь: Martin Geisler
| дата: чт 01 апр. 12:29:19 2010 +0200
| резюме: переименовать.
|
| o changeset: 1: 9d06fa155634
| / пользователь: Martin Geisler 
| дата: чт 01 апр. 12:29:18 2010 +0200
| резюме: обновление.
|
o changeset: 0: d08bcebadd9e
   пользователь: Martin Geisler 
   дата: чт 01 апр. 12:29:18 2010 +0200
   резюме: начальный импорт.

Выход слияния:

объединение hello.en.txt и hello.txt в hello.en.txt
0 файлов обновлено, 1 файлов объединено, 0 файлов удалено, 0 файлов не решено
(слияние веток, не забудьте зафиксировать)

Другими словами: Mercurial взял изменение из ревизии 1 и объединил его с новым именем файла из ревизии 2 ( hello.en.txt). Разумеется, обработка этого случая необходима для поддержки рефакторинга, а рефакторинг - это именно то, что вы захотите сделать в ветке.

Мартин Гайслер
источник
+1 для подробного примера можно нажать на клавиатуру и посмотреть, что получится. Как Mercurial Noob, мне интересно, следует ли версия hg этого примера очевидным образом, строка за строкой?
DarenW
4
@DarenW: я добавил соответствующие команды Mercurial, надеюсь, это прояснит ситуацию!
Мартин Гейслер
17

Не говоря об обычных преимуществах (коммиты в автономном режиме, процесс публикации , ...), вот пример «слияния», который мне нравится:

Основной сценарий, который я продолжаю видеть, - это ветка, в которой ... фактически разрабатываются две несвязанные задачи
(она началась с одной функции, но привела к разработке этой другой функции.
Или она началась с патча, но это привело к разработка другой функции).

Как вам слить только одну из двух функций на основной ветке?
Или как вы изолируете две функции в своих собственных ветках?

Вы можете попытаться сгенерировать какие-то патчи, проблема в том, что вы больше не уверены в функциональных зависимостях. которые могли бы существовать между:

  • коммиты (или ревизии для SVN), используемые в ваших патчах
  • другой совершает не часть патча

Git (и Mercurial, я полагаю) предлагает опцию rebase --onto для перебазирования (сброса корня ветви) части ветви:

Из поста Джефроми

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

Вы можете распутать ситуацию, когда у вас есть патчи для v2, а также новая функция wss:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

, что позволяет вам:

  • протестируйте каждую ветку изолированно, чтобы проверить, все ли компилируется / работает как задумано
  • объединить только то, что вы хотите, чтобы основной.

Еще одна особенность, которая мне нравится (которая влияет на слияния), - это возможность фиксировать коммиты (в ветке, еще не перенесенной в другое хранилище), чтобы представить:

  • более чистая история
  • коммиты, которые являются более связными (вместо commit1 для function1, commit2 для function2, снова commit3 для function1 ...)

Это обеспечивает слияния, которые намного проще, с меньшим количеством конфликтов.

VonC
источник
svn не имеет офлайн коммитов? ROFL? как кто-нибудь может даже удаленно рассмотреть возможность его использования, если это так?
о0 '.
@Lohoris Когда вышел SVN, широко не использовались DVCS с открытым исходным кодом; на данный момент, я думаю, это в основном инерция, что люди все еще используют его.
Макс Нанаси
@MaxNanasy очень плохой вид инерции ... все же, выбирать его сейчас было бы просто глупо.
о0 '.
@Lohoris Online (точнее, централизованные) коммиты не такая уж большая проблема в небольшой команде, где хранилище может просто находиться на общем локальном сервере. DVCS были в основном разработаны для больших, географически распределенных команд (git и mercurial предназначались для управления кодом ядра Linux) и проектов с открытым исходным кодом (отсюда и популярность GitHub). Инерция также может рассматриваться как оценка рисков по сравнению с преимуществами изменения инструмента, являющегося центральным элементом рабочего процесса команды.
IMSoP
1
@Lohoris Я думаю, что вы неправильно поняли мою точку зрения о БД, брандмауэре и т. Д., Я не имею особого смысла в том, что я могу сделать коммит на моей домашней машине, если я не могу запустить этот код первым. Я мог бы работать вслепую, но тот факт, что я не могу что-то совершать, не будет главным препятствием для меня.
IMSoP
8

Мы недавно мигрировали из SVN в GIT и столкнулись с такой же неопределенностью. Было много неофициальных доказательств того, что GIT был лучше, но трудно было найти какие-либо примеры.

Я могу сказать вам, однако, что GIT намного лучше при слиянии, чем SVN. Это, очевидно, анекдотично, но есть таблица для подражания.

Вот некоторые из вещей, которые мы нашли:

  • SVN обычно создавал много конфликтов в деревьях в ситуациях, когда казалось, что этого не должно быть. Мы никогда не доходили до сути, но этого не происходит в GIT.
  • Хотя лучше, GIT значительно сложнее. Потратьте некоторое время на тренировки.
  • Мы привыкли к черепахе SVN, которая нам понравилась. Черепаха GIT не так хороша, и это может отпугнуть вас. Однако теперь я использую командную строку GIT, которую я предпочитаю Tortoise SVN или любой из GIT GUI.

Когда мы оценивали GIT, мы проводили следующие тесты. Они показывают GIT как победителя, когда дело доходит до слияния, но не настолько. На практике разница намного больше, но я думаю, нам не удалось воспроизвести ситуации, которые SVN плохо обрабатывает.

GIT vs SVN Оценка слияния

cedd
источник
5

Другие охватили более теоретические аспекты этого. Может быть, я могу дать более практическую перспективу.

В настоящее время я работаю в компании, которая использует SVN в модели разработки "ветки". То есть:

  • Никакая работа не может быть сделана на стволе
  • Каждый разработчик может создать свои собственные ветви
  • Филиалы должны длиться в течение всего времени выполнения задачи
  • У каждой задачи должна быть своя ветка
  • Слияния обратно в транк необходимо авторизовать (обычно через bugzilla)
  • В случаях, когда необходимы высокие уровни контроля, привратник может выполнять слияния

В общем, работает. SVN можно использовать для такого потока, но он не идеален. Есть некоторые аспекты SVN, которые мешают и формируют поведение человека. Это дает некоторые негативные аспекты.

  • У нас было довольно много проблем с людьми, разветвляющимися от точек ниже, чем ^/trunk. Это засоряет записи информации о слиянии по всему дереву и в конечном итоге нарушает отслеживание слияния. Ложные конфликты начинают появляться, и царит путаница.
  • Собрать изменения из ствола в ветку относительно просто. svn mergeделает то, что вы хотите. Для --reintegrateобъединения ваших изменений требуется (как нам сказали) команда слияния. Я никогда по-настоящему не понимал этот переключатель, но это означает, что ветвь не может быть снова объединена с транком. Это означает, что это мертвая ветка, и вы должны создать новую, чтобы продолжить работу. (Смотрите примечание)
  • Весь процесс выполнения операций на сервере через URL-адреса при создании и удалении веток действительно сбивает с толку и пугает людей. Поэтому они избегают этого.
  • Переключение между ветвями легко ошибиться: одна часть дерева смотрит на ветвь А, а другая часть смотрит на ветвь B. Поэтому люди предпочитают выполнять всю свою работу в одной ветке.

Обычно случается, что инженер создает филиал в первый день. Он начинает свою работу и забывает об этом. Через некоторое время приходит босс и спрашивает, может ли он выпустить свою работу в багажник. Инженер боялся этого дня, потому что реинтеграция означает:

  • Слияние его долгоживущей ветки обратно в транк и решение всех конфликтов, и выпуск несвязанного кода, который должен был быть в отдельной ветке, но не был.
  • Удаляя свою ветку
  • Создание новой ветки
  • Переключение его рабочей копии на новую ветку

... и поскольку инженер делает это как можно меньше, он не может вспомнить "магическое заклинание", чтобы сделать каждый шаг. Происходят неправильные переходы и URL-адреса, и они внезапно оказываются в беспорядке и получают «эксперта».

В конце концов все успокаивается, и люди учатся исправлять недостатки, но каждый новый стартер сталкивается с одними и теми же проблемами. Возможная реальность (в отличие от того, что я изложил в начале):

  • На стволе нет работы
  • У каждого разработчика есть одна крупная ветка
  • Ветви длятся до тех пор, пока работа не будет освобождена
  • У исправленных ошибок, как правило, есть своя ветка
  • Слияние с туловищем выполняется после авторизации

...но...

  • Иногда работа превращается в магистраль, когда не должна, потому что она находится в той же ветке, что и другая.
  • Люди избегают всех слияний (даже простых вещей), поэтому люди часто работают в своих маленьких пузырьках
  • Большие слияния, как правило, происходят и вызывают ограниченное количество хаоса.

К счастью, команда достаточно мала, чтобы справиться, но она не будет масштабироваться. Дело в том, что ни одна из этих проблем не является проблемой с CVCS, но более того, поскольку слияния не так важны, как в DVCS, они не так удобны. Это «трение слияния» вызывает поведение, которое означает, что модель «Feature Branch» начинает разрушаться. Хорошие слияния должны быть свойством всех VCS, а не только DVCS.


В соответствии с этим теперь есть --record-onlyпереключатель, который можно использовать для решения --reintegrateпроблемы, и, по- видимому, v1.8 выбирает, когда выполнять реинтеграцию автоматически, и это не приводит к тому, что ветвь впоследствии становится мертвой.

Пол С
источник
Насколько я понимаю, опция --reintegrate сообщает svn, что вы уже исправили конфликтующие изменения при слиянии с веткой объектов. По сути, вместо того, чтобы рассматривать его как патч, он перезаписывает целые файлы версией ветки, уже проверив в истории слияния, что все версии ствола были объединены в ветку.
IMSoP
@IMSoP: возможно, в этом есть какой-то смысл. Это не объясняет мне, почему это было необходимо или почему это сделало невозможным дальнейшее слияние из этой ветви. Не помогло то, что вариант тоже был в значительной степени недокументирован.
Пол С
Я использовал его только через TortoiseSVN, где это всегда было заметно в интерфейсе слияния. Я считаю, что SVN 1.8 автоматически выбирает правильную стратегию и не нуждается в отдельной опции, но я не знаю, исправили ли они нормальный алгоритм слияния, чтобы правильно обрабатывать ветку, которая была сброшена таким образом.
IMSoP
3

До Subversion 1.5 (если я не ошибаюсь) у Subversion был существенный недостаток в том, что он не запоминал историю слияний.

Давайте посмотрим на случай, изложенный VonC:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - A - x (v2-only)
           \
             x - B - x (wss)

Обратите внимание на ревизии A и B. Скажем, вы слили изменения из ревизии A в ветке "wss" в ветку "v2-only" в ревизии B (по любой причине), но продолжили использовать обе ветки. Если вы попытаетесь снова объединить две ветви, используя mercurial, он объединит изменения только после ревизий A и B. В Subversion вам придется объединить все, как если бы вы не делали слияние раньше.

Это пример из моего собственного опыта, где слияние из B в A заняло несколько часов из-за объема кода: это было бы настоящей болью для повторения , что было бы в случае с Subversion pre-1.5.

Еще одно, вероятно, более существенное отличие в поведении слияний от Hginit: перевоспитание Subversion :

Представьте, что вы и я работаем над каким-то кодом, и мы разветвляем этот код, и каждый из нас уходит в свои отдельные рабочие пространства и вносит в этот код множество изменений, так что они сильно расходятся.

Когда нам нужно объединиться, Subversion пытается просмотреть обе ревизии - мой измененный код и ваш измененный код - и пытается угадать, как их объединить в один большой безобразный беспорядок. Обычно это дает сбой, создавая страницы и страницы «конфликтов слияния», которые на самом деле не являются конфликтами, просто места, где Subversion не удалось выяснить, что мы сделали.

Напротив, пока мы работали отдельно в Mercurial, Mercurial был занят сохранением серии изменений. И поэтому, когда мы хотим объединить наш код, Mercurial на самом деле имеет гораздо больше информации: он знает, что каждый из нас изменил и может применить эти изменения, а не просто смотрит на конечный продукт и пытается угадать, как его поместить. все вместе.

Короче говоря, метод анализа различий в Mercurial (был?) Превосходит метод Subversion.

Томислав Накич-Альфиревич
источник
5
Я прочитал Hginit. Жаль, что он не показывает больше практических примеров того, как hg работает лучше, чем svn. По сути, он говорит «доверять Джоэлу», что hg просто лучше. простые примеры, которые он показал, возможно, можно было бы сделать и с svn ... собственно, поэтому я и открыл этот вопрос.
Stmax
1
Основываясь на том, как это сказано, напрашивается наивный вопрос: что, если алгоритм слияния Mercurial был введен в Subversion? Будет ли SVN так же хорошо, как HG? Нет, потому что преимущество hg заключается в организации более высокого уровня, а не в низкоуровневой текстовой математике слияния строк из файлов. Это новая идея, которую мы, svn-пользователи, должны использовать.
DarenW
@stmax: Я понимаю, что ты имеешь в виду. Однако мнение Джоэла или кого-либо еще не имеет значения: одна технология либо лучше другой (для набора вариантов использования), либо нет. @DarenW и @stmax: исходя из моего личного опыта, Hg выигрывает руки благодаря своей распределенной работе (я не подключен все время), производительности (много локальных операций), чрезвычайно интуитивному ветвлению, усиленному превосходным алгоритмом слияния, Откат hg, шаблонный вывод журнала, hg glog, отдельная папка .hg ... Я мог бы просто продолжать, и продолжать, и продолжать ... все, что угодно, кроме git и bazaar, похоже на стритджек.
Томислав Накич-Альфиревич
Комментарий hg, процитированный по поводу "changesets", кажется мне довольно неточным. SVN прекрасно знает, какие изменения он объединяет (набор изменений - это, по сути, разница между двумя снимками, и наоборот, верно?), И может применять каждый из них по очереди, если того пожелает; конечно, не нужно ничего «угадывать». Если это создает «один большой нечестивый беспорядок», то это проблема реализации, а не что-то фундаментальное для дизайна. Основная проблема, которую трудно решить поверх текущей архитектуры, - это перемещение / копирование / переименование файла.
IMSoP