Принцип DRY в действии
При разработке новых проектов очень часто приходится сталкиваться с необходимостью повторного использования наработок других проектов. Когда такого общего кода было немного, то переносить файлы с одного проекта на другой казалось несложно. Но как и любой Copy-Paste, даже самое небольшое дублирование кода всегда ведет к проблемам.Поэтому в определенный момент мы озадачились выносом общего функционала в отдельные репозитории. Тем, как это получилось осуществить с использованием подрепозиториев Mercurial, и хотелось бы поделиться в этой статье.
Какие у нас альтернативы
Использовать общий код в проектах можно в скомпилированном виде или в виде исходных файлов.
- Для подключения общего кода в виде dll удобно использовать менеджер пакетов Nuget. Так же с его помощью можно поставлять отладочную информацию. Но он работает только для .NET, а в нашей фирме есть проекты на других платформах.
- Использование исходного кода позволяет производить отладку интерактивно с возможностью модификации. И у нас часто возникает необходимость изменения общего кода при работе в рамках проекта.
Для подключения к проекту общего кода в виде исходных файлов удобно использовать подрепозитории.
Как это работает
Идея - главный репозиторий хранит в себе ссылки на используемые подрепозитории и на нужные их версии.
Слева относительный путь до подрепозиториев на диске, справа - адреса их родителей. Файл .hgsubstate хранит состояние подрепозиториев, а именно - ревизии, на которую обновлены подрепозитории. Вручную обычно не редактируется. Выглядит она примерно так:Commons/Common = http://src/CommonCommons/CommonSys = http://src/CommonSys
a33509b3d9464c9a99b5c17759d6fb23e307ccc6 Commons/CommonПри работе с основным репозиторием мы всегда видим изменения в подрепозиториях.
2c38e78a41aa2f1e2825bb0bbae624a61363e2ba Commons/Common
Рис. 1 Отображение изменений в подрепозитории.
Одним коммитом мы можем зафиксировать изменения в главном репозитории и всех подрепозиториях. На самом деле их будет столько, сколько всего репозиториев, но время и комментарий будут совпадать.
При клонировании родительского репозитория на локальную машину автоматически затягиваются подрепозитории и кладутся в папки, записанные в .hgsub
Схема использования
Вот преимущества использования подрепозиториев:- Каждый репозиторий может быстро переключаться между версиями подрепозиториев;
- Мы всегда имеем непосредственный доступ к исходникам общего кода, которые можно напрямую включить в наш проект;
- При затягивании репозитория с сервера вместе с ним затягиваются все подрепозитории (в т.ч. и подрепозитории подрепозиториев, если такие есть)
Но что, если “Общий код 1” использует “Общий код?”
На клиентской машине налицо дублирование кода, ни к чему хорошему не приведёт. Поэтому мы можем убрать ссылку из “Нашего проекта” на “Общий код” и перенастроить ссылки на проекты “Общего кода”, находящихся в составе “Общего кода 1”:
В конечном итоге получается довольно большая вложенность подрепозиториев. При внесении изменений и последующем использовании Rebase в подрепозиториях возникают проблемы с появлением ссылок на несуществующие ревизии [более подробное описание в статье по Rebase], а также поддержка такой вложенности непроста.
Разработчики Mercurial рекомендует использование тонких репозиториев, которые служат только для хранения остальных репозиториев в качестве подрепозиториев:
Тонкий репозиторий всего лишь хранит информацию о связи разных версий подрепозиториев.
Создаём свой первый подрепозиторий
Чтобы Mercurial правильно определил текущую ревизию подрепозиториев, давайте определим последовательность действий:- Копируем репозитории, которые хотим сделать подрепозиториями, в нужное место каталога основного репозитория.
- Добавляем папку через меню в черепахе (см. рисунок 2).
- В создавшемся файле .hgsub путь родительского репозитория нового подрепозитория будет совпадать с относительным путём до него, исправляем на действительный путь до родителя.
- Пункты 2 и 3 можно сделать через командную строку или просто добавлением файла .hgsub, тогда не придётся его исправлять, сразу создаём правильный:
$ echo "ПодРепа1 = http://src/Subrepo1" > .hgsub
$ hg add .hgsub - Всё, можно фиксировать изменения.
Рис. 2 Добавление подрепозитория.
Особенности использования
- Создание ветки в основном репозитории никак не повлияет на подрепозитории и наоборот.
- Если кто-то другой внесёт изменения в подрепозитории, версия нашего подрепозитория не изменится, если мы не обновимся в нём на новую версию или если кто-то не сделает это за нас и не внесёт изменения в файл .hgsubstate в основном репозитории.
Чего мы лишаемся без подрепозиториев?
Можно задаться вопросом, зачем нам именно такая технология? Что нам мешает просто клонировать репозитории общего кода рядом с репозиторием проекта и не использовать подрепозитории? Мы так же будем выбирать нужную нам версию. Чего мы лишаемся:- Никто кроме нас не будет знать, какую версию общего кода мы используем;
- Не сможем централизованно следить за незафиксированными изменениями;
- Новый сотрудник должен будет предварительно узнать, какие репозитории необходимо получить и на какие ревизии обновиться, чтобы начать работу с проектом.
- В конфигурации билд-сервера необходимо настраивать работу со всеми репозиториями, вместо одного.
В любом случае нет универсального решения. Выбор нужно осуществлять, исходя из ваших потребностей. Надеюсь, описанный способ поможет сделать правильный выбор.