Pull to refresh

DVCS and DAGs

Reading time 11 min
Views 5.3K
Original author: Eric Sink
Перевод статьи Эрика Синка (Eric Sink) — DVCS and DAGs (Part 1 and Part 2).

Прим. переводчика: В этой статье я буду ис­поль­зо­вать ори­гиналь­ные анг­ло­языч­ные сокращения DVCS и DAG для обозначения расп­ре­делён­ных систем контроля версий (Distributed Version Control System — DVCS) и нап­равлен­ных ацикличных графов (Directed Acyclic Graph — DAG).


Часть 1


Есть две категории людей:
  1. Те, кто пытается делить всё на две категории.
  2. Те, кто этого не делает.

Я один из первых. :-)

Есть две категории систем контроля версий:
  1. Те, в которых история — это Линия.
  2. Те, в которых история — это Нап­равлен­ный Ацикличный Граф (DAG).

Тра­дици­он­ные инструменты (такие как Subversion и Vault) тяготеют к ведению истории как Линии. В DVCS (таких как Git и Mercurial) история — это DAG. Разница между этими двумя моделями довольно интересна.

Линейная модель испытана и работает. История — это пос­ле­дова­тель­ность версий, одна за другой.
1761_image001

Чтобы создать новую версию необходимо:
  1. Получить последнюю версию.
  2. Внести изменения.
  3. За­фик­си­ровать (commit) сделанные изменения.

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

Но у линейной модели есть одна большая проблема: вы можете создать новую версию только в том случае, если она основана на последней версии. И часто случается следующее:
  1. Я получаю из репозитория последнюю версию. В момент, когда я её получил, это была версия 3.
  2. Я вношу туда каике-то изменения.
  3. Пока я это делаю, кто-то создаёт версию 4.
  4. Когда я собираюсь за­фик­си­ровать мои изменения, я не могу этого сделать, так как они не основаны на текущей версии. «Базой» для моих изменений была версия 3, которая была актуальной на тот момент, когда я начал работу.

1761_image002

Линейная модель истории не позволит мне создать версию 5 так, как показано на картинке. Вместо этого я буду вынужден получить изменения, сделанные от 3-ей к 4-й версии, и «слить» их со своей версией.

Очевидный вопрос: Что случится, если мы разрешим за­фик­си­ровать 5-ю версию на основе 3-ей? Наша история перестанет быть Линией. И превратится в DAG.

А почему мы должны это делать?

Основным пре­иму­щест­вом DAG-модели в том, что она не прерывает раз­ра­бот­чи­ка в тот момент, когда он пытается за­фик­си­ровать результат своей работы. В этом плане DAG — это, возможно, более точное отражение того, что реально происходит в команде, прак­ти­ку­ющей па­рал­лель­ную разработку (здесь использован не совсем точный перевод термина «concurrent development», в котором акцент делается на том, что параллельно про­из­во­димые раз­ра­бот­чи­ками изменения часто делаются в одном и том же коде и часто конфликтуют — прим. пер.). Версия 5 была основана 3-ей, так почему бы не отразить этот факт?

Правда, выясняется, что есть причины этого не делать. В этом графе мы не знаем, какая версия «последняя». И это приводит ко множеству проблем:
  • Предположим, нам нужны изменения в версиях 4 и 5 для выпуска релиза. В данный момент мы не можем этого получить. В системе нет версии, которая включала бы оба набора изменений.
  • Наша система сборки настроена на ав­то­мати­чес­кую сборку последней версии. Что она должна делать в этой ситуации?
  • Даже если мы соберём обе версии, 4-ю и 5-ю, какая из них должна быть передана в QA для тес­ти­рова­ния?
  • Если разработчик хочет обновить своё дерево до последней версии, какую из них ему предпочесть?
  • Когда разработчик хочет сделать какие-то изменения, какую версию он должен взять за основу?
  • Наш менеджер проекта хочет знать, какие задачи выполнены и сколько работы ещё осталось сделать. Его понимание «сделано» очень тесно связано с понятием «последний». Если он не может понять, какая версия последняя, его мозг плавится в попытках обновить диаграмму Гантта.

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

Дабы избежать со­сущест­во­вания собак и кошек в состоянии непрерывной массовой истерии, средства, ис­поль­зу­ющие DAG-модель ведения истории, делают всё возможное, чтобы помочь нам разрешить путаницу. Ответ тот же, что и для линейной модели — нам нужно слияние (merge). Но вместо того, чтобы требовать от раз­ра­бот­чи­ка делать слияние перед фиксацией изменений, мы позволяем сделать это слияние позднее.

1761_image003

Кому-то требуется создать версию, включающую все изменения, сделанные в версиях 4 и 5. Когда эта версия будет за­фик­си­рова­на, она будет содержать стрелки, указывающие на обеих её «родителей».

Порядок восс­та­нов­лен. Мы снова знаем, какая из версий «последняя». Если кто-нибудь вспомнит о том, что надо бы пе­резаг­ру­зить нашего менеджера, он скорее всего тут же сообразит, что этот граф выглядит почти как линия. И за исключением того, что между версиями 3 и 6 случилось что-то непонятное и запутанное, это и есть Линия. Но самое лучшее для нашего менеджера — сильно не переживать по этому поводу.

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

&@#!

И что теперь?

Если вы живёте в рамках линейной модели, этот граф для вас — абсолютный кошмар. У него ЧЕТЫРЕ оконечных узла. Всё, что требует понимания того, какая версия является последней, обречено на провал, включая вы­ше­опи­сан­но­го менеджера, который сейчас, вероятно, свернулся калачиком в своём кабинете и надеется, что мама не забыла положить ему любимые печенюшки к макаронам.

Линейная модель выглядит в такие моменты весьма прив­ле­катель­ной. И это хорошее обоснование того, что 99,44% раз­ра­бот­чи­ков используют SCM-средства, основанные на линейной модели ведения истории (да, я выдумал эту статистику).

Но всё же, несмотря на весь этот очевидный хаос, мы должны напомнить себе об основном пре­иму­щест­ве DAG-модели: она более точно описывает реальный ход дел в работе прог­раммис­та. Она не заставляет раз­ра­бот­чи­ков прогибаться под свои желания, как это делает линейная модель. Когда разработчик хочет что-то за­фик­си­ровать — он это делает. И DAG попросту записывает ровно то, что дей­стви­тель­но произошло.

Многие команды всегда будут пред­по­читать линейную модель. И в этом нет ничего плохого. Жизнь проще при таком подходе.

Но для некоторых других команд DAG-модель может оказаться весьма полезной. А каким-то командам она вообще может достаться «в нагрузку». Просто потому, что им потребуется ис­поль­зо­вать DVCS по каким-то иным причинам. DVCS-средства используют DAG-модель потому, что у них нет выбора. Если мы не можем пред­по­лагать наличия постоянного соединения с центральным сервером, у нас нет другого пути и мы не можем заставить раз­ра­бот­чи­ков вписывать всю свою работу в линейную модель.

Поэтому нам нужно найти пути, как управляться с DAG. И как же нам быть?

Один из вариантов — это рест­рук­ту­риро­вать каждую операцию. Если вы говорите доктору «это настоящая пытка, когда возникает не­об­хо­димость определить последнюю версию», доктор скажет вам «прекратите пытаться это делать». Вместо этого всегда точно указывайте, какой узел ис­поль­зо­вать:
  • Система сборки не собирает последний узел. Вместо этого она собирает ровно тот, который мы ей укажем. Или, может быть, она собирает каждый узел.
  • QA тестирует те сборки, которые кто-то считает необходимым про­тес­ти­ровать.
  • Раз­ра­бот­чи­ки не обновляют своё дерево до «последнего». Вместо этого, они смотрят на граф, выбирают узел, и обновляются до него.

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

Но как нам указать узел? Одно из обс­то­ятель­ств, которое делает этот подход проб­ле­матич­ным, — это то, что эти узлы часто имеют сложные имена. К примеру, в Git имя узла — это что-то вроде e69de29bb2d1d6434b8b29ae775ad8c2e48c5391. Раз­ра­бот­чи­ки находят такой способ именования не слишком интуитивно-понятным.

Все DVCS-средства используют DAG. И все они делают много разных вещей, чтобы-либо пре­дотв­ра­тить «кризис мно­жест­вен­ных оконечных узлов», либо помочь команде управляться с этим. Но все они делают это немного по-своему.

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


Часть 2


На первую часть этой статьи я получил два вида зас­лу­жива­ющих внимания отзывов (Эрик Синк, автор ори­гиналь­ной статьи, опубликовал её в двух частях с разницей во времени в одну неделю, чем и объясняется наличие реакции читателей отдельно на её первую часть — прим. пер.):
  1. Несколько людей обвинили меня в расп­рос­тра­нении страха, не­уве­рен­ности и сомнений (в оригинале автор использует расп­рос­тра­нён­ную сленговую абб­ре­ви­ату­ру FUD — Fear Uncertainly and Doubt, не имеющую прямого аналога в русской речи, — прим. пер.) в отношении линейной модели, потому как я лишь вскользь упомянул о проблемах с DAG-моделью и остановился буквально в шаге от того, чтобы заявить, что DAG-модель может стать лекарством от рака, остановить глобальное потепление и принести мир и спокойствие на Ближний Восток.
  2. Некоторые люди спрашивали, как я нарисовал такие классные диаграммы.

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

Мой ответ в защиту DVCS


Да, моя компания (SourceGear, — прим. пер.) раз­ра­баты­ва­ет систему контроля версий (Vault), основанную на линейной модели ведения истории. Поэтому любая DVCS — это, в оп­ре­делён­ной степени, прямой конкурент моему продукту.

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

Моя мама с лёгкостью подтвердит, что я не всегда хорошо соблюдаю правила:-)

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

На самом деле.

Но здесь происходит нечто большее, чем просто я в роли ка­пита­лис­ти­чес­ко­го мятежника.

Фанаты Git, вам стоит немного остыть.

Серьёзно, ярые защитники Git делают этот мир непригодным для жизни. Git в самом деле отличная штука, но просто он не является правильным выбором во всех без исключения ситуациях.

В их защиту надо признать, что в этом вопросе яблоко от яблони упало недалеко. Когда люди начинают ин­те­ресо­вать­ся DVCS, одной из первых вещей, на которую они натыкаются, становится видео с пре­зен­та­ци­ей Линуса Торвальдса о Git, записанное в 2007-м году. И там они видят человека, который, кажется, тоже этого не понимает.

Парни, Subversion — это, возможно, самое популярное на данный момент средство контроля версий в мире. Без малого каждый, кто использует систему контроля версий сегодня, использует ту, что основана на линейной модели ведения истории. И они используют эти средства успешно и продуктивно. Когда кто-то от­ка­зыва­ет­ся признавать какую-либо пригодность этой модели, они выглядят неумными.

Видео Торвальдса наделало много вреда. Такая позиция — это большое ра­зоча­рова­ние для людей, ин­те­ресу­ющих­ся тем, что нового появляется в мире контроля версий.

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

Но если вы в самом деле хотите помочь миру увидеть пре­иму­щест­ва Git и аналогичных средств, тогда осознайте, что люди делали свою работу продуктивно и до их появления.

Мой ответ по поводу тех классных диаграмм


Эти картинки были нарисованы художником-ил­люст­ра­тором компании SourceGear, Джоном Вулли (John Woolley), который также создал все иллюстрации к книге комиксов «Evil Mastermind». Всю офор­ми­тель­скую работу и создание иллюстраций для моей готовящейся к выходу книге об управлении исходными кодами также делает Джон.

Тем не менее, поскольку картинки Джона получили гораздо больше похвалы, чем моя «тысяча слов», я решил поз­лобс­тво­вать и отказался включать какие-либо его иллюстрации во вторую часть статьи. :-)

ОК, давайте побольше поговорим про графы


Как я уже говорил в первой части, если DAG-у позволить расти без всякого управления, всё может прев­ра­тить­ся в настоящую неразбериху. DAG-и проще создавать. Линии проще ис­поль­зо­вать. Как только мы начинаем по максимуму ис­поль­зо­вать DAG-модель, чтобы за­дей­ство­вать все её пре­иму­щест­ва, следующее, что сразу же происходит — нам хочется вернуть Линии обратно.

Вот почему в любой DVCS есть возможности, которые могут быть ис­поль­зо­ваны для того, чтобы рост DAG-а был управляемым. Эти возможности придуманы для того, чтобы помешать раз­ра­бот­чи­кам фиксировать изменения, избегая при этом всякой от­ветс­твен­ности за порождаемую за­путан­ность, которая растёт каждый раз, когда мы создаём новую точку ветвления.

Другими словами, каждая DVCS содержит возможности, которые позволяют раз­ра­бот­чи­ками взять фрагмент графа и трактовать его как линию.

Git


Git управляет ростом DAG-а путём поддержки именованных веток (branches). Вы лишены возможности за­фик­си­ровать изменения, если их «родитель» — не оконечный узел («лист»).

Таким образом, если я использую команду Git checkout, чтобы обновить мою рабочую копию репозитория до узла, не являющегося оконечным, Git проявляет вежливую заботу обо мне:
eric$ git checkout 9542b
Note: moving to "9542b" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b
HEAD is now at 9542b5f... initial

Если вы фиксируете изменения, всегда основанные только на оконечном узле, то ваша история остаётся очень похожей на линию.

Mercurial


Исторически, Mercurial описывался как под­держи­ва­ющий только одну ветку на один репозиторий. Сравнения с Git часто фо­куси­рова­лись на очевидном недостатке — отсутствии поддержки ветвления внутри одного репозитория (здесь перевод не со­от­ветс­тву­ет ори­гиналь­но­му тексту, так как в ком­мента­ри­ях к статье автор признал наличие опечатки в этом месте, — прим. пер.).

Я говорю в прошедшем времени, так как я слышал, что в Mercurial добавлены некоторые возможности в этой области.

Я упомянул Mercurial здесь, чтобы не оставить и его фанатов в стороне. Я не могу говорить с позиции большого опыта работы с этой системой.

Но всё же я считаю возможным упоминать Mercurial как подт­вер­жде­ние моей точки зрения: в ранних (по меньшей мере) версиях Mercurial управлял ростом графа, препятствуя его ветвлению. К тому же, это спо­собс­тво­вало всеобщему восприятию Mercurial как очень простого в ис­поль­зо­вании средства.

Bazaar


Этот инструмент — это DVCS, которую я использовал больше всего, но я всё же не могу пока считать себя экспертом по ней. По моему опыту, я бы оха­рак­те­ризо­вал Bazaar как систему, которая усердно трудится над контролем за ростом DAG-а.

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

Но, что пожалуй здорово, Bazaar даёт мне возможность не ис­поль­зо­вать центральный сервер, как настоящая DVCS. Но в этом режиме действуют те же самые ключевые ограничения: я не могу за­фик­си­ровать какие-либо изменения, если они не основаны на конечном узле в репозитории.

Когда я использую Bazaar, я испытываю ощущение, что использую средство с линейной моделью.

Мои пред­почте­ния


В этом отношении мне ближе всего подход Git к решению этой проблемы.

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

Мне нравится способность Git переключать мою локальную копию репозитория через команду «git checkout имя_ветки». Я понимаю, что людям, не привыкшим к постоянной мысли о DAG-е, эта возможность кажется непонятной. Но мне она нравится.

Попрошу заметить, что мне по-прежнему нравятся средства с линейной моделью, такие как Subversion и Vault. Я просто хочу сказать, что средство с DAG-моделью должно действовать похожим образом.

Fossil


DVCS, которая в последнее время интригует меня больше всего — это Fossil. Она написана Ричардом Хиппом (D. Richard Hipp), создателем SQLite.

У Fossil есть ряд интересных воз­можнос­тей. Наиболее су­щест­вен­ная — это встроенная поддержка баг-трекинга. Это область, в которой все остальные DVCS уступают ей. Они дают вам расп­ре­делён­ное управление версиями, но когда приходит время раз­ра­бот­чи­ку обновить информацию в баг-трекере, мир снова становится цент­ра­лизо­ван­ным.

Так или иначе, я пока только лишь начинаю пристально прис­матри­вать­ся к Fossil, но мне нравится как на его сайте описана проблема ветвления:
Иметь в дереве более одного оконечного узла обычно считается не­жела­тель­ным, поэтому обычно ветвлений либо полностью избегают, как на рис. 1, либо быстро устраняют, как на рис. 3 (имеются ввиду рисунки в этой статье, а не на сайте fossil, — прим. пер.). Но иногда кому-то требуется иметь несколько оконечных узлов. К примеру, проект может иметь один конечный узел, который предс­тав­ля­ет последнюю версию раз­ра­баты­ва­емой версии продукта, а второй узел — последнюю «стабильную» (про­тес­ти­рован­ную) версию. Когда несколько оконечных узлов це­лесо­об­разны — мы называем это ветвлением, а не форком. (Тут важно по­чувс­тво­вать разницу между анг­ло­языч­ны­ми терминами branch и fork. Автор хочет подчеркнуть по­ложи­тель­ный смысл термина branch (ветка) и в какой-то степени от­ри­цатель­ный характер форка (fork), — прим. пер.).

Прекрасно. В результате у меня создалось впечатление, что Fossil в этом отношении работает аналогично Git. Когда DAG раз­ветв­ля­ет­ся, сложность уве­личи­ва­ет­ся.
Tags:
Hubs:
+29
Comments 18
Comments Comments 18

Articles