Слияние: Hg/Git по сравнению с SVN

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

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

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

Ответ 1

Я не использую 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 она просто фиксирует объекты, которые могут иметь более одного родителя.

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

    *---*---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", а затем снова объединены в ветку "A" как "M2".

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

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

    Git обрабатывает эту ситуацию просто на практике, используя "рекурсивную" стратегию слияния. Я не уверен в Mercurial.

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

    Оба Git и Mercurial обрабатывают такой случай на практике: Git с использованием определения переименования, Mercurial с помощью переименования.

НТН

Ответ 2

Я тоже искал случай, когда, скажем, 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). Разумеется, обработка этого случая необходима для поддержки рефакторинга и рефакторинга - это именно то, что вам нужно делать на ветке.

Ответ 3

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

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

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

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

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

Git (и, предположительно, Mercurial), предложите опцию rebase --onto для перераспределения (reset корневой части ветки) ветки:

От сообщение Джефоми

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

что позволяет:

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

Другая функция, которая мне нравится (которая влияет на слияния), - это способность squash commits (в ветке еще не нажата другая репо), чтобы представить:

  • более чистая история
  • совершает более согласованные (вместо commit1 для функции1, commit2 для функции2, commit3 снова для функции1...)

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

Ответ 4

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

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

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

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

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

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

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

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

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

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

... но...

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

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


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

Ответ 5

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

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

Вот некоторые из найденных вещей:

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

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

GIT vs SVG Merging Evaluation

Ответ 6

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

Посмотрите на случай, обозначенный 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 (по какой-либо причине), но продолжали использовать обе ветки. Если вы попытались объединить две ветки снова, используя меркурий, это только сменит изменения после ревизий A и B. С подрывной деятельностью вам придется объединить все, как будто вы не делали слияния раньше.

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

Другая, вероятно, более релевантная разница в поведении слияния из Hginit: переподготовка Subversion:

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

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

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

Короче говоря, Меркурийский способ анализа различий (был?) превосходит подрывную деятельность.