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

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

141 голосов
/ 19 марта 2010

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

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

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

Ответы [ 6 ]

119 голосов
/ 21 марта 2010

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

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

cd /tmp
rm -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 'Create branch.'
svn switch '^/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 'Rename on branch.'
svn switch '^/trunk' .
svn merge --reintegrate '^/branches/rename'

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

--- Merging differences between repository URLs into '.':
A    hello.en.txt
   C hello.txt
Summary of conflicts:
  Tree conflicts: 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
|  tag:         tip
|  parent:      0:d08bcebadd9e
|  user:        Martin Geisler 
|  date:        Thu Apr 01 12:29:19 2010 +0200
|  summary:     Rename.
|
| o  changeset:   1:9d06fa155634
|/   user:        Martin Geisler 
|    date:        Thu Apr 01 12:29:18 2010 +0200
|    summary:     Update.
|
o  changeset:   0:d08bcebadd9e
   user:        Martin Geisler 
   date:        Thu Apr 01 12:29:18 2010 +0200
   summary:     Initial import.

Результат слияния:

merging hello.en.txt and hello.txt to hello.en.txt
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

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

90 голосов
/ 19 марта 2010

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

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

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

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

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

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

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

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

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

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

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

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

    *---b-----B1--M1--*---M3
         \     \ /        /
          \     X        /
           \   / \      /
            \--B2--M2--*
    

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

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

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

НТН

17 голосов
/ 19 марта 2010

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

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

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

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

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

    С пост Джефроми

    - 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 ...)

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

7 голосов
/ 28 августа 2015

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

Я могу вам сказать, что GIT НАМНОГО ЛУЧШЕ при слиянии, чем SVN. Это явно анекдотично, но есть таблица для подражания.

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

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

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

GIT vs SVN Merging Evaluation

5 голосов
/ 13 апреля 2013

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

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

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

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

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

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

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

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

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

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

... но ...

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

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


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

3 голосов
/ 19 марта 2010

До 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 заняло несколько часов из-за объема кода: было бы очень трудно снова пройти через , что было бы дело с подрывной пре-1.5.

Другое, возможно, более существенное отличие в поведении слияния от Hginit: перевоспитание Subversion :

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

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

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

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

...