Твердження, чому об’єднання краще в 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
Коли ми хочемо об'єднати зміни 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 and pull ).
Перше, що ви робите під час роботи з DVCS - це клонувати сховища (git clone
, hg clone
та bzr branch
). Клонування - це те саме, що створення гілки в контролі версій. Деякі називають це розгалуженням або розгалуженням (хоча останні часто також використовуються для позначення суміщених гілок), але це саме те саме. Кожен користувач працює у власному сховищі, що означає, що у вас відбувається розгалуження кожного користувача .
Структура версії - це не дерево , а скоріше графік . Більш конкретно, спрямований ациклічний графік (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
's, що в даному випадку не відповідає. Будь-яка спроба натиснення призведе до того, що система скаже щось схоже на "А-а ... Боюся, не можу дозволити вам зробити це Боб ".
Таким чином, Боб повинен здійснити і потім об'єднати зміни (з git's pull
; або hg's 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<--+
Існує ще один варіант , щоб об'єднати в мерзотникові і рт.ст., називається перебазуватися , which'll перемістити зміни Боба в після новітніх змін. Оскільки я не хочу, щоб ця відповідь була більш багатослівною, я дам вам змогу прочитати документи про git , mercurial або базар про це.
Як вправу для читача, спробуйте скласти, як це буде працювати з іншим залученим користувачем. Це робиться аналогічно як приклад вище з Боб. Об’єднання між сховищами простіше, ніж ви думаєте, оскільки всі зміни / комісії однозначно ідентифікуються.
Існує також проблема надсилання патчів між кожним розробником, що було величезною проблемою в Subversion, яка пом'якшується в git, hg та bzr шляхом однозначних ідентифікаційних змін. Після того, як хтось об'єднав свої зміни (тобто здійснив злиття) і відправить їх усім іншим в команді, щоб спожити, натиснувши в центральний сховище або відправивши патчі, тоді їм не доведеться турбуватися про злиття, оскільки це вже сталося . Мартін Фаулер називає такий спосіб роботи безладною інтеграцією .
Оскільки структура відрізняється від Subversion, вона замість цього використовує DAG, вона дозволяє розгалуження та злиття проводити простішим способом не лише для системи, але й для користувача.