Ответ 1
Утверждение о том, почему слияние лучше в DVCS, чем в Subversion, во многом было основано на том, как ветвление и слияние работали в Subversion некоторое время назад. Subversion до 1.5.0 не хранит никакой информации о том, когда ветки были объединены, поэтому, когда вы хотели слить, вам нужно было указать, какой диапазон изменений, которые должны были быть объединены.
Итак, почему Subversion сливается сосать?
Обдумайте этот пример:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
Когда мы хотим merge, b1 меняет в туловище, что мы выпустим следующую команду, стоя в папке с проверкой соединительной линии из:
svn merge -r 2:7 {link to branch b1}
..., который попытается объединить изменения из b1
в ваш локальный рабочий каталог. И затем вы фиксируете изменения после устранения любых конфликтов и проверяете результат. При фиксации дерева ревизий будет выглядеть так:
1 2 4 6 8 9
trunk o-->o-->o---->o---->o-->o "the merge commit is at r9"
\
\ 3 5 7
b1 +->o---->o---->o
Однако этот способ определения диапазонов ревизий быстро выходит из-под контроля, когда дерево версий растет, поскольку подрывная деятельность не содержит метаданных о том, когда и какие изменения слились вместе. Подумайте, что будет дальше:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
В основном это проблема дизайна репозитория, который имеет Subversion, для создания ветки вам нужно создать новый виртуальный каталог в репозитории, в котором будет размещена копия соединительной линии, но она не хранит никакой информации о когда и какие вещи снова слились. Это иногда приводит к неприятным конфликтам слияния. Что еще хуже, так это то, что Subversion использовала двухстороннее слияние по умолчанию, что имеет некоторые ограничения в автоматическом слиянии, когда две ветки ветки не сравниваются с их общим предком.
Чтобы смягчить эту Subversion, теперь хранятся метаданные для ветки и слияния. Это правильно разрешит все проблемы?
И, кстати, Subversion все еще сосет...
В централизованной системе, такой как подрывная деятельность, виртуальные каталоги сосут. Зачем? Потому что у каждого есть доступ, чтобы просмотреть их... даже мусорные экспериментальные. Ветвление хорошее, если вы хотите поэкспериментировать , но вы не хотите экспериментировать с вашими тетками. Это серьезный когнитивный шум. Чем больше веток вы добавите, тем больше дерьма вы увидите.
Чем больше государственных веток у вас в репозитории, тем сложнее будет отслеживать все разные ветки. Таким образом, вопрос, который у вас будет, заключается в том, что ветка все еще находится в разработке или если она действительно мертва, что трудно сказать в любой централизованной системе контроля версий.
В большинстве случаев, из того, что я видел, организация по умолчанию будет использовать одну большую ветку. Какой позор, потому что, в свою очередь, будет сложно отслеживать версии тестирования и выпуска, а также что бы то ни было хорошее от ветвления.
Итак, почему DVCS, например Git, Mercurial и Bazaar, лучше, чем Subversion при ветвлении и слиянии?
Существует очень простая причина: ветвление - это первоклассная концепция. Нет никаких виртуальных каталогов по дизайну, а ветки - это жесткие объекты в DVCS, которые должны быть такими, чтобы работать просто с синхронизацией репозиториев (т.е. Push и pull).
Первое, что вы делаете, когда работаете с DVCS, - клонировать репозитории (git clone
, hg clone
и bzr branch
). Клонирование концептуально то же самое, что создание ветки в управлении версиями. Некоторые называют это разветвление или разветвление (хотя последнее часто также используется для обозначения совместно расположенных ветвей), но это одно и то же. Каждый пользователь запускает собственный репозиторий, что означает, что вы продолжаете разветвление каждого пользователя.
Структура версии не дерево, а скорее graph. Более конкретно направленный ациклический график (DAG, что означает график, который не имеет циклов). Вам действительно не нужно останавливаться на специфике DAG, кроме того, что каждая команда имеет одну или несколько родительских ссылок (на основе которых была основана фиксация). Таким образом, следующие графики показывают стрелки между ревизиями в обратном направлении из-за этого.
Очень простой пример слияния будет таким; представьте себе центральный репозиторий под названием origin
и пользователь Алиса, клонирующий репозиторий к своей машине.
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
Что происходит во время клонирования, так это то, что каждая ревизия копируется в Алису точно так, как она была (которая проверена однозначно идентифицируемым идентификатором хэша), и отмечает, где находятся ветки происхождения.
Затем Алиса работает над своим репо, совершая в своем собственном репозитории и решает подтолкнуть ее изменения:
a… b… c…
origin o<---o<---o
^ master
"what'll happen after a push?"
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
Решение довольно просто, единственное, что нужно сделать репозиторию origin
, - это взять все новые ревизии и перенести его ветвь на новую версию (которая git вызывает "быструю перемотку вперед" ):
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
В примере использования, который я проиллюстрировал выше, даже не нужно объединять что-либо. Таким образом, проблема действительно заключается не в слиянии алгоритмов, поскольку алгоритм трехстороннего слияния практически одинаковый между всеми системами управления версиями. Проблема больше связана с структурой, чем чем-либо.
Итак, как насчет того, чтобы вы показали мне пример с реальным слиянием?
Правда, приведенный выше пример - очень простой пример использования, поэтому позволяет сделать гораздо более скрученный, хотя и более общий. Помните, что origin
началось с трех версий? Ну, парень, который их сделал, позвонил ему Бобу, работал самостоятельно и сделал фиксацию в своем собственном хранилище:
a… b… c… f…
bob o<---o<---o<---o
^ master
^ origin/master
"can Bob push his changes?"
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Теперь Боб не может напрямую перенаправить свои изменения в репозиторий origin
. Как система обнаруживает это, проверяя, что Боб пересмотрит прямые спуска с origin
, что в этом случае нет. Любая попытка подтолкнуть приведет к тому, что система скажет что-то похожее на "Э-э... Я боюсь, не могу позволить вам сделать этого Боба. "
Итак, Бобу нужно втягивать, а затем объединять изменения (с помощью git pull
; hg pull
и merge
; или bzr merge
). Это двухэтапный процесс. Сначала Боб должен получить новые версии, которые будут скопировать их, как они из репозитория origin
. Теперь мы можем видеть, что график расходится:
v master
a… b… c… f…
bob o<---o<---o<---o
^
| d… e…
+----o<---o
^ origin/master
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Второй шаг процесса вытягивания состоит в объединении расходящихся кончиков и совершении фиксации результата:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
Надеемся, что слияние не столкнется с конфликтами (если вы их ожидаете, вы можете выполнить два шага вручную в git с помощью fetch
и merge
). Что еще нужно сделать, так это снова включить эти изменения в origin
, что приведет к ускоренному слиянию, поскольку комманда слияния является прямым потомком последнего в репозитории origin
:
v origin/master
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
v master
a… b… c… f… 1…
origin o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
Существует еще один вариант слияния в git и hg, называемый rebase, который переместит изменения Боба после новых изменений. Поскольку я не хочу, чтобы этот ответ был более подробным, я дам вам прочитать git, mercurial или bazaar docs об этом.
Как упражнение для читателя, попробуйте выяснить, как он будет работать с другим вовлеченным пользователем. Аналогичным образом это делается как пример выше с Бобом. Слияние между репозиториями проще, чем вы думаете, потому что все ревизии/фиксации однозначно идентифицируются.
Также существует проблема отправки патчей между каждым разработчиком, что является огромной проблемой в Subversion, которая смягчается в Git, hg и bzr с помощью однозначно идентифицируемых ревизий. Когда кто-то объединил свои изменения (т.е. Совершил слияние) и отправил их для всех остальных в команде, чтобы потреблять, либо нажав на центральный репозиторий, либо отправив патчи, тогда им не нужно беспокоиться о слиянии, потому что это уже произошло, Мартин Фаулер называет этот способ работы неразборчивой интеграции.
Поскольку структура отличается от Subversion, вместо этого использует DAG, она позволяет легче и проще выполнять разветвление и слияние не только для системы, но и для пользователя.