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

Вот код, который вы можете использовать :), попробуйте и скажите мне:

^([0-2][0-9]|(3)[0-1])(\/)(((0)[0-9])|((1)[0-2]))(\/)\d{4}$

141
задан Martin Geisler 24 February 2012 в 19:53
поделиться

4 ответа

Я сам не использую Subversion, но из release notes для Subversion 1.5: Merge tracking (foundational) следует, что есть следующие отличия от того, как работает отслеживание слияний в полных 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 основана на changeset), в то время как в Git и Mercurial это просто объекты коммитов, которые могут иметь более одного родителя.

  • "Known Issues" подраздел для отслеживания слияний в 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', а затем снова слита в ветвь 'A' как 'M2'.

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

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

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

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

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

HTH

91
ответ дан 23 November 2019 в 23:00
поделиться

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

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

Еще одно, вероятно, более важное отличие в поведении слияния от Hginit: Subversion Re-education :

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

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

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

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

3
ответ дан 23 November 2019 в 23:00
поделиться

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

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

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

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

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

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

Из сообщения Jefromi

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

, позволяя вам:

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

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

  • более чистую историю
  • коммиты, которые более последовательны (вместо коммита1 для функции1, коммита2 для функции2, коммита3 снова для функции1...)

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

17
ответ дан 23 November 2019 в 23:00
поделиться

Я тоже искал случай, когда, скажем, 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'

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

120
ответ дан 23 November 2019 в 23:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: