Darcs и Pijul. Системы контроля версий для тех, кто не любит деревья

8cb3ee188563b6ab3bbeddf6ef8f7343.png

Для наведения порядка в своих делах или для организации совместной работы над проектами я долгое время пользовался системой git, которая на сегодняшний день стала де-факто стандартной и самой распространённой системой контроля версий. Мой первый репозиторий на GitHub датируется 2012 годом, однако, в течение всех этих лет, вместе с навыком и опытом использования git, накапливалась усталость и некоторое раздражение от неё. И дело тут не в самой системе, как таковой, а в том, что мне нелегко даётся мышление деревьями или ацикличными направленными графами — основной структурой в модели git. Когда к древообразной структуре добавляются операции merge и rebase, то оно постепенно превращается в каучуконосный фикус, и теряет первоначальную индуктивную природу.

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

Конечно же, git далеко не единственная система контроля версий. У неё есть с десяток альтернатив разной степени навороченности, и из них я захотел выбрать наиболее подходящую. Я стал искать не самую популярную или самую крутую, а самую понятную мне систему управления версиями. В качестве основного критерия для выбора была прозрачность концепции.

Идеальные, для меня системы нашлись в знакомом и близком мне мире математики и computer science. Они имеют в основе гомологическую теорию изменений (patch theory) и исчисление изменений (patch calculus), в которых изменения в файлах и папках, называемые патчами, образуют полноценную алгебру, гарантирующую, там где это теоретически возможно, детерминистичность и идентичность версий, расположенных в разных удалённых репозиториях. Речь идёт о двух системах распределённого контроля версий:  Darcs, написанной на Haskell, и Pijul, написанной на Rust. Обе они сейчас активно развиваются и предлагают свои сетевые репозитории. Оказалось, что про них на Хабре толком нет ничего, тогда как про git образовался целый хаб. Поскольку я люблю и использую Haskell, я остановил свой выбор на Darcs, и вот, спустя два месяца непрерывной работы над библиотекой геометрической алгебры для hackage, я готов поделиться впечатлениями от её использования.

6a9d1d5ac3a7f29f69c4322e95d30d53.png

Эта система предоставляет все основные базовые функции: отслеживание изменений в проекте, возможности откатить состояние на произвольное число шагов, организацию безопасного экспериментирования с вариантами кода или текста через клонирование и слияние репозиториев, а также интеграцию с Emacs, в котором я, собственно, и работаю. На освоение базовых команд и просмотр пары роликов на YouTube у меня ушло часа два. После чего работа пошла гладко. Настройка соединения с сервером удалённого репозитория через SSH, после мучений с современной системой аутентификации GitHub, вызвала только тёплую благодарность за простоту и понятность. Конечно, пригодился опыт работы с git, но надо отдать должное, документация и интерактивное взаимодействие с пользователем у Darcs на высоте. К тому же система не перегружена функциями, но те, что у неё есть, она выполняет хорошо.

Главная особенность систем Darcs и Pijul состоит в том, что вместо деревьев и снимков состояния репозиториев, они оперируют линейными цепочками отдельных изменений в файлах и папках репозитория. Для того чтобы навести порядок в этих изменениях и корректно их сливать при совместной работе используется алгебраический подход. Он заключается в том, что поскольку отдельные изменения, как правило, обратимы и коммутативны, есть возможность перегруппировывать и переставлять цепочки изменений, не меняя при этом их суммарного результата. Разумеется, при этом есть ряд ограничений, налагаемых отношением частичного порядка, который образуют зависимости между отдельными изменениями. В результате состояние репозитория можно определить неупорядоченным множеством патчей, которые представляют собой цепочки упорядоченных фрагментов (hunks).

Такой подход позволяет корректно

  • исключать и добавлять патчи, не нарушая целостности репозитория, получая новое состояние, и при этом не заботиться об их порядке;

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

  • избегать древообразности внутреннего представления репозитория, как если бы любые слияния сопровождались применением команды git rebase .

К сожалению, полностью избежать конфликтов при слиянии изменений невозможно даже теоретически. Это связано с тем, что коммутируют, всё‑таки, не все изменения и не всякий направленный ацикличный граф можно вытянуть в цепочку без потери информации. Среди патчей существуют контекстно‑зависимые пары обратимых, но не коммутирующих элементов. Простейший пример: создание файла и внесение в него изменений нельзя поменять местами. Однако такие типы патчей легко распознаются и система, в случае конфликта, достаточно внятно объясняет что произошло. Вообще, Darcs по умолчанию весьма говорливая система.

За десять лет существования и развития Darcs разработчикам удалось преодолеть ряд математических сложностей, к которым приводит прямое использование алгебры патчей. В частности, «комбинаторные взрывы», которые наблюдались в версии 1 и 2, приводя к зависанию системы, и создали дурную славу Darcs. Однако в 2020 году с этой проблемой успешно справились, введя и последовательно расширя типизацию патчей, а также используя концепцию кодекартовых квадратов (pushout) из теории категорий. По этому пути сразу пошли в разработке Pijul, что позволило избежать ряда граблей. Подробнее об этом можно посмотреть тут.

Я намеренно не стал в этой заметке приводить элементы теории патчей, хотя она и небезынтересна, чтобы не возникло ощущение, что без неё использовать Darcs нельзя. Также как нет необходимости знать про матрицы и кватернионы, чтобы резаться в 3D-шутер, абстракции в Darcs и Pijul «не протекают» и не мешают. Тем, кому интересно, я могу посоветовать статьи в блоге математика Joe Neeman: Часть 1, Часть 2, или даже целую книжку The Darcs Book.

К недостаткам Darcs можно отнести отсутствие виртуальных веток (branches в git). Надо сказать, в сообществе разработчиков до сих пор нет единого мнения, нужны ли в этой системе вообще. Для введения новых экспериментальных изменений, для которых чаще всего и используются ветки, Darcs создаёт клон репозитория в отдельной директории в файловой системе. После тестирования, этот клон сливается с тем репозиторием, который считается стабильным или готовым к релизу, после чего удаляется,  либо остаётся для истории, если идея не пошла дальше эксперимента. Таким образом, ветки не заполняют собой индекс основного репозитория, оставляя его максимально чистым. А обзор «веток» происходит в простом файловом менеджере.

dca29738b065102eda82000416647843.png

После развитой системы виртуальных веток в git, такой подход сначала кажется примитивным. Но после двух месяцев работы, первой пары сотен коммитов в семи экспериментальных ветках, я всецело его принял. Во‑первых, он интуитивен, надёжен и прост как пробка и запутаться в реальных, а не виртуальных ветках практически невозможно. Во‑вторых, все «живые» ветки явно видны в локальном репозитории и доступны одновременно, без необходимости в графическом интерфейсе. А в‑третьих, удаление ненужной ветки тривиально, безболезненно и не требует никаких операции типа git rebase, в силу детерминистичности слияний. При необходимости, любой из клонов можно сохранить на сервере hub.darcs и не бояться потерять локально.

Кроме того, для Darcs нет GUI и каких-то средств визуализации, но поскольку визуализировать линейную цепочку легко и в терминале командой darcs log, ничего графического для просмотра репозитория особо и не нужно. Дерево зависимостей патчей можно построить с помощью команды darcs show dependencies | dot -Tpdf -o FILE.pdf

Сейчас я использую его для написания книги, подготовки материалов к своему блогу и в разработке библиотеки для hackage. Pijul, вообще, позиционируется авторами как система контроля версий не для программистов и даже скорее для не-программистов: дизайнеров, писателей, архитекторов и т.д. В общем, после дюжины лет возни с git, как с профессиональным фотоаппаратом, я с облегчением разрешил себе просто «снимать на телефон», осознав, что я не работаю в крупной корпорации и не собираюсь писать ядро Linux.

© Habrahabr.ru