Организация Git-репозиториев с общими вложенными подмодулями


50

Я большой поклонник подмодулей Git . Мне нравится иметь возможность отслеживать зависимость вместе с ее версией, чтобы вы могли выполнить откат к предыдущей версии вашего проекта и иметь соответствующую версию зависимости для безопасного и чистого построения. Более того, проще выпустить наши библиотеки как проекты с открытым исходным кодом, поскольку история библиотек отделена от истории приложений, которые зависят от них (и которые не будут открыты).

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

Предположим, пара приложений: studioи player, и зависимые библиотеки core, graphи network, где зависимости следующие:

  • core автономный
  • graphзависит от core(подмодуль в ./libs/core)
  • networkЗависит от core(подмодуль в ./libs/core)
  • studioзависит от graphи network(субмодули в ./libs/graphи ./libs/network)
  • playerзависит от graphи network(субмодули в ./libs/graphи ./libs/network)

Предположим, что мы используем CMake, и у каждого из этих проектов есть модульные тесты и все работы. Каждый проект (включая studioи player) должен иметь возможность компилироваться отдельно для выполнения метрик кода, модульного тестирования и т. Д.

Дело в том, что рекурсивно git submodule fetch, тогда вы получите следующую структуру каталогов:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/graph/
studio/libs/graph/libs/         (sub-module depth: 2)
studio/libs/graph/libs/core/
studio/libs/network/
studio/libs/network/libs/       (sub-module depth: 2)
studio/libs/network/libs/core/

Обратите внимание, что coreдважды клонируется в studioпроекте. Помимо этой траты дискового пространства у меня есть проблема с системой сборки, потому что я собираюсь coreдважды, и у меня потенциально могут быть две разные версии core.

Вопрос

Как организовать подмодули так, чтобы получить версионную зависимость и автономную сборку без получения нескольких копий общих вложенных подмодулей?

Возможное решение

Если зависимость от библиотеки является чем-то вроде предположения (то есть в моде «известно, что она работает с версией X» или «официально поддерживается только версия X») и потенциальные зависимые приложения или библиотеки отвечают за сборку с любой версией, которая им нравится, тогда Я мог бы представить следующий сценарий:

  • Соберите систему сборки graphи networkскажите им, где найти core(например, через компилятор включите путь). Определите две цели сборки: «автономный» и «зависимость», где «автономный» основан на «зависимости» и добавляет путь включения, указывающий на локальный coreподмодуль.
  • Ввести дополнительную зависимость: studioот core. Затем выполняет studioсборку core, устанавливает путь включения для своей собственной копии coreсубмодуля, затем выполняет сборку graphи находится networkв режиме «зависимости».

Полученная структура папок выглядит следующим образом:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/core/
studio/libs/graph/
studio/libs/graph/libs/         (empty folder, sub-modules not fetched)
studio/libs/network/
studio/libs/network/libs/       (empty folder, sub-modules not fetched)

Тем не менее, для этого требуется некоторое волшебство системы сборки (я уверен, что это можно сделать с помощью CMake) и небольшая ручная работа со стороны обновлений версий (обновление graphможет также потребовать обновления coreи networkполучения совместимой версии coreво всех проектах) ,

Есть мысли по этому поводу?


Обратите внимание, что эта проблема не характерна для cmake: она существует для любой системы сборки, в том числе и для системы! (т. е. когда предполагается, что супер-проект просто добавляет исходные библиотеки; в него входят библиотеки только для заголовков)
MM

Ответы:


5

Я очень опаздываю на эту вечеринку, но на ваш вопрос все еще не дан полный ответ, и это довольно заметный хит от Google.

У меня точно такая же проблема с C ++ / CMake / Git / Submodules, и у меня есть аналогичная проблема с MATLAB / Git / Submodules, которая получает некоторые дополнительные странности, потому что MATLAB не компилируется. Я недавно наткнулся на это видео , которое, кажется, предлагает «решение». Мне не нравится решение, потому что оно по сути означает выбрасывание субмодулей, но оно устраняет проблему. Это так же, как @errordeveloper рекомендует. Каждый проект не имеет подмодулей. Чтобы построить проект, создайте супер-проект для его сборки и включите его в качестве родственного элемента в его зависимости.

Так что ваш проект для разработки graphможет выглядеть так:

buildgraph/graph
buildgraph/core

и тогда ваш проект для студии может быть:

buildstudio/studio
buildstudio/graph
buildstudio/network
buildstudio/core

Суперпроекты - это просто основной CMakeLists.txtи куча подмодулей. Но ни в одном из проектов нет подмодулей.

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


1

Я предполагаю, что когда вы интегрируете оба graphи networkподмодули в studio, вы всегда должны иметь одну и ту же версию coreв данный момент в истории studio. Я бы связал studio/libs/coreсубмодуль с studio/libs/{graph,network}/libs.

Обновить:

Я создал несколько репозиториев с указанными вами зависимостями:

./core      <--- (v2)
./graph
./graph/libs
./graph/libs/core  <--- (v2)
./graph/.gitmodules
./network
./network/libs
./network/libs/core  <--- (v1)
./network/.gitmodules
./studio
./studio/libs
./studio/libs/graph
./studio/libs/graph/libs
./studio/libs/graph/libs/core <--- (v1)
./studio/libs/graph/.gitmodules
./studio/libs/network
./studio/libs/network/libs
./studio/libs/network/libs/core  <--- (v1)
./studio/libs/network/.gitmodules
./studio/studio
./studio/.gitmodules

v1и v2две разные версии core. graphобрабатывает версию 2, в то время как networkтребует некоторой работы и застревает на версии 1. В studioлокальных встроенных версиях coreобоих указывает v1на наличие работающей программы. Теперь, кроме перспективы сборки, все хорошо работает с подмодулями.

Теперь я могу удалить следующий каталог:

./studio/libs/network/libs/core

И замените его символической ссылкой:

./studio/libs/network/libs/core@ -> ../../graph/libs/core/

Я локально фиксирую это изменение и теряю способность иметь две отдельные версии coreвнутри studio, но я строю только coreодин раз. Когда v2я буду готов к обновлению , я могу сделать:

 git submodule update # (--rebase ?)

... внутри студии / библиотеки / сети.


Идея символической ссылки пришла мне в голову, но это не решение проблемы. Если вы ссылаетесь graph/libs/coreизвне, вы не используете субмодуль. Если вы ссылаетесь studio/libs/coreна одну из собственных библиотек субмодуля, то какую вы выбираете, graphили network? Кроме того, что происходит, когда его глубина составляет три или более слоев? Наконец, что, если, coreможет быть диапазон изменений. Это не очевидно , что вы хотите , чтобы ссылка на любую версию , coreчто graphи networkиспользуете.
Андре Карон

"Какой из них вы выбираете?" : coreбудет подмодулем, извлеченным из исходной coreбиблиотеки, обновленным до версии, совместимой с обоими graphи network(вы должны решить, какая из них хороша). Символические ссылки будут добавлены в локальный graphи networkсубмодулей (не выбран).
coredump

1
Символические ссылки, которые вы предлагаете добавить, graphи которые networkбудут указывать за пределы своего собственного хранилища (например, где-то еще в studioпроекте). Как они узнают, когда использовать свой собственный подмодуль, а когда использовать символическую ссылку? Возможно, вам следует добавить пример, чтобы продемонстрировать свое мышление.
Андре Карон

0

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


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

3
Вы должны уточнить свой ответ, чтобы продемонстрировать, как он решает различные проблемы. Мне не совсем понятно, как вы связываете зависимости, учитывая, что в зависимости от контекста зависимые библиотеки не находятся в одном месте.
Андре Карон

0

Я бы не использовал подмодули.

Это заманчиво, как и в случае с svn-externals. Тем не менее, можете ли вы быть уверены, что все те проекты, на которые вы ссылаетесь, останутся на том же месте в течение года? Что насчет пяти?

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

По сути, у меня есть структура папок следующим образом:

myproject/... [sources etc]
ext/ [third-party dependencies]


e.g. ext/boost, ext/cppunit

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

Кроме того, есть множество проблем с подмодулями, как описано здесь


Я уверен, что они в нужном месте, потому что я поддерживаю их все :-) Кроме того, будьте осторожны при копировании проектов из-за условий перераспределения.
Андре Карон

ОК, это уменьшает проблему. И лицензирование: да, вы должны быть осторожны, но это совсем другая проблема.
Уилберт

0

Столкнувшись с точно такой же проблемой здесь. Одним из решений может быть иметь некоторые репо , libsкоторые будут держать core, network, graphкак подмодули и только CMakeLists , что бы сказать каждому из LIBS где найти его зависимости. Каждое приложение теперь будет иметь libsподмодуль и использовать только необходимые библиотеки.

Тестирование каждой библиотеки может быть настроено двумя способами:

  • Имейте core_testing, graph_testing, network_testing как отдельные приложения
  • Разверните проверенные библиотеки на тестовых серверах и найдите их во время выполнения тестов с использованием cmake

Разве это не делает все библиотеки доступными для всех других библиотек?
Андре Карон,

По умолчанию да. Но это может быть решено в cmakelists уровня libs. Если graphне нужно знать о network- не передавайте networkсвязанный материал в graphsubdir
Max
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.