LINUX.ORG.RU

Альтернатива git submodule

 


1

5

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

./appA
./appA/deps/
./appA/deps/libA
./appA/deps/libA/deps/
./appA/deps/libA/deps/libB
./appA/deps/libA/deps/libC
./appA/deps/libD
./appA/deps/libD/deps/
./appA/deps/libD/deps/libE
./appA/deps/libD/deps/libF

На самом деле, структура сложнее и вложенность даже больше. Приложение и каждая библиотека хранится в отдельном git-репозитории. Между собой собой сейчас проекты соединены при помощи стандартного механизма git submodule и разработка превратилась в АДЪ. При любой модификации какого-нибудь глубоко вложенного модуля, напр., libE, приходится делать коммиты во все проекты, которые его содержат (libD, appA), причем в правильном порядке.

Была идея сделать один основной рутовый мета-проект, в котором будут лежать как подмодули остальные проекты, а между проектами связь разорвать. Тогда при обновлении любого проекта, придется делать коммит только в него и рутовый проект.

Но git не позволяет пересекать подпроекты. Т.е. так можно:

В проекте libA
git submodule add git@mygit/libB deps/libB
git submodule add git@mygit/libC deps/libC

В проекте appA
git submodule add git@mygit/libA deps/libA

appA --> libA --> libB
              \-> libC

А так нельзя

В проекте appA
git submodule add git@mygit/libA deps/libA # все нормально
git submodule add git@mygit/libB deps/libA/deps/libB # ругается

appA --> libA
     \-> libA/deps/LibB

fatal: Pathspec 'deps/libA/deps/libB' is in submodule 'libA'
Failed to add submodule 'deps/libA/deps/libB'

Т.е. простым git submodule придется делать плоскую структуру, что осложнит жизнь и несколько неправильно.

Может кто-нибудь знает альтернативу? На ум приходит только Гугловский gclient из их depot_tools при помощи которого они контролируют разработку хрома, но он несколько монструозен и слабо документирован.

Deleted
Ответ на: комментарий от x3al

Тогда пропадает весь смысл системы контроля версий. Я не смогу точно сказать, из чего был собран проект 25 января 2014г в 18:35. Придется долго бродить по проектам, вспоминать, какие тогда использовались зависимости, откатывать на их то состояние.

У меня есть старые проекты где так заведено. Это не круто.

Deleted
()
Ответ на: комментарий от Deleted

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

x3al ★★★★★
()
Ответ на: комментарий от Deleted

Придется долго бродить по проектам, вспоминать, какие тогда использовались зависимости, откатывать на их то состояние.

Что ведь уже и решалось с помощью:

При любой модификации какого-нибудь глубоко вложенного модуля, напр., libE, приходится делать коммиты во все проекты, которые его содержат (libD, appA), причем в правильном порядке.

Интересно, нельзя ли это автоматизировать однострочником?

В принципе,

Я не смогу точно сказать, из чего был собран проект 25 января 2014г в 18:35

можно решить просто добавив в систему сборки для каждого проекта git describe + timestamp. Хотя вот эту зависимость от git на этапе сборки любят не все, и лучше, чтобы она была опциональной.

gag ★★★★★
()
Ответ на: комментарий от Deleted

Вообще, некоторые либо жёстко скриптуют submodule, либо переходят на subtree, но оба варианта — не решение.

x3al ★★★★★
()
Ответ на: комментарий от x3al

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

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

Или написать свой велосипед

Deleted
()
Ответ на: комментарий от gag

Ага. А потом из этого «частого релиза» выковыривать исходники проекта. Т.к. тестировщики говорят, что в версии, выпущенной в прошлом году все работало, а потом сломалось.

Deleted
()

наверняка у тебя есть причины, но почему нельзя просто слить все submodules в 1 репозиторий (хоть тем же subtree)?

или если перефразировать — какова техническая причина хранить все «зависимости» в разных репах?

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

waker ★★★★★
()

еще, если два разных проекта настолько тесно пересекаются — может быть имеет смысл их тоже слить в 1 репозиторий, и отказаться от submodules?

waker ★★★★★
()

3й вариант пришедший в голову.. вылить все зависимости в отдельный репозиторий/submodule (1 шт), и его подключать в оба проекта appA и appB. тоже нормальная тема, сам так делаю в некоторых случаях.

waker ★★★★★
()

А чем плохо хранить все в одной репе?

anonymous
()
Ответ на: комментарий от waker

наверняка у тебя есть причины, но почему нельзя просто слить все submodules в 1 репозиторий (хоть тем же subtree)?

сливать в один репозиторий - тоже порочный круг.

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

2) git log на таком проекте будет выдавать ад из миллиона строк добавляемых новых проектов.

Deleted
()
Ответ на: комментарий от waker

3й вариант пришедший в голову.. вылить все зависимости в отдельный репозиторий/submodule (1 шт), и его подключать в оба проекта appA и appB. тоже нормальная тема, сам так делаю в некоторых случаях.

Это все хорошо, пока у тебя не появится appC, которая содержит в себе appB. Тогда модификация репозитория с зависимостями потребует обновления appA, appB и appC

Deleted
()
Ответ на: комментарий от Deleted

Это все хорошо, пока у тебя не появится appC, которая содержит в себе appB.

в таком случае, лично я бы просто все 3 проекта в одном репозитории держал.

waker ★★★★★
()
Ответ на: комментарий от waker

Да, сейчас глянул на Pro Git описание subtree. Я им никогда не пользовался. Нужно будет дома поэкспериментировать.

Как в таком репозитории проектах будут храниться теги объединенных проектов?

Deleted
()
Ответ на: комментарий от Deleted

Тогда пропадает весь смысл системы контроля версий. Я не смогу точно сказать, из чего был собран проект 25 января 2014г в 18:35. Придется долго бродить по проектам, вспоминать, какие тогда использовались зависимости, откатывать на их то состояние.

А если суперпроект, содержащий сабмодули? Как сделано в Qt:

http://code.qt.io/cgit/qt/qt5.git/tree/

EXL ★★★★★
()
Последнее исправление: EXL (всего исправлений: 1)
Ответ на: комментарий от EXL

А если суперпроект, содержащий сабмодули? Как сделано в Qt:

Про это я говорил в описании (мой план рутового мета-проекта). Но из-за ограничений гита такие прокты нельзя вкладывать друг в друга.

Напр., в том же QT, qtdocgallery возможно зависит от qtdoc, но их приходится держать на обном уровне, не вкладывая друг в друга.

Deleted
()
Ответ на: комментарий от Deleted

Как в таком репозитории проектах будут храниться теги объединенных проектов?

сожалею, но не знаю ответа.

waker ★★★★★
()
Ответ на: комментарий от EXL

В hg (Mercurial) есть методы решения этой проблемы?

по-моему, в hg даже вменяемого аналога submodules нет..

во всяком случае, у нас в конторе используется собственный велосипед, функционально идентичный git submodule.

waker ★★★★★
()

При любой модификации ... приходится

Нет. В том смысле, что это непрактично и нужды в этом в большинстве случаев нет. Когда делаешь релиз, проставляешь тег, вносишь сильно ломающие изменения - тогда да, имеет смысл, а в остальном бестолковая возня.

Тред не читал.

anonymous
()

На ум приходит только Гугловский gclient из их depot_tools при помощи которого они контролируют разработку хрома

Хромой и ведроид может и так контролируют, но 95% кода гугла лежит в едином мегарепозитории, и на конференциях гугловцы рассказывают как это круто (continues integration во все поля, отсутствие diamond-зависимостей (когда один подпроект через другие косвенно зависит от разных версий третьего), etc). Так что если все проекты — ваши собственные, может задуматься о едином репе?
Git log, кстати, прекрасно работает так: $ git log /path/to/subproject

Tayler ★★
()
Ответ на: комментарий от Deleted

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

Завести отдельный «проект» с процессом сборки, где хранить какая версия чего откуда вытаскивается. Тогда достаточно будет вытаскивать его(?состоянием на?), и запустить, остальное оно само добудет из сколь угодно плоских-недовложенных реп.

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

В общем-то, вопрос про то, есть ли популярные готовые для этого инструменты, а не накаленные велосипеды.

Весь мир то как-то живет, продукты как-то разрабатывает.

Deleted
()
Ответ на: комментарий от Tayler

Так что если все проекты — ваши собственные, может задуматься о едином репе?

Нет, используется куча сторонних библиотек. Поэтому существует проблема в том, что иногда хочется в них вносить изменения и при этом иметь возможность эти либы обновлять. Т.е. точно знать, какие изменения мои, а какие сторонние. + не хочется в своем проекте видеть сотни коммитов от сторонних разработчиков.

Git log, кстати, прекрасно работает так: $ git log /path/to/subproject

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

Deleted
()

Мы используем ExternalProject от cmake. В проекте зависимости от Poco, пары своих либ. Конечно не maven dependencies, но работает 😀

vega
()
Ответ на: комментарий от vega

Мы используем ExternalProject от cmake. В проекте зависимости от Poco, пары своих либ. Конечно не maven dependencies, но работает 😀

Как происходит процесс отладки зависимостей проекта?

Вот напр., у тебя есть приложение appA и оно зависит от библиотеки libA, которая в свою очередь зависит от libB

appA --> libA --> libB

Ты собираешь проект и видишь, что в нем бага. Т.е. нужно вносить изменения в libA и в libB и смотреть, как все это работает. После того, как все это заработало, нужно сделать коммиты в appA, libA и libB.

ExternalProject стягивает конкретную ревизию или весь гитовый проект и делает checkout нужной ревизии? Можно ли переключаться между режимами сборки Release/Debug?

Deleted
()
Ответ на: комментарий от Deleted

+ cmake все же не до конца универсален. Плюсовые проекты им еще можно собирать (не смотря на его марсианскую логику), но как независящее от языка решение оно не годиться. Для андроидовских приложений лучше использовать gradle, для нодовских - npm. Оборачивать все это в cmake лишь для контроля целостности сборки - как-то странно.

Deleted
()
Ответ на: комментарий от Deleted

Нам для упрощения разработки кросс-платформенного софта на C++ пришлось сделать собственный велосипед на Ruby, который успешно используется уже около полугода в повседневной работе. Принцип такой: указывается откуда и что забирать (это может быть URL архива или URL репозитория (git, hg, svn), а затем что и куда из этого копировать. Грубо говоря, указывается URL архива библиотеки, эта библиотека скачивается, распаковывается в отдельном каталоге, а оттуда уже нужные тебе куски копируются в нужные тебе подкаталоги. Чуть подробнее на эту тему: http://http//eao197.blogspot.com.by/2016/04/prog-mxxru-1610-mxxruexternals.html и http://eao197.blogspot.com.by/2016/04/prog-mxxruexternals.html

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

  • если библиотека задействуется в нескольких проектах, то в каждый проект она будет скопирована отдельно;
  • за версиями библиотеки нужно следить самостоятельно (т.е. если у библиотеки появилась новая версия, то эту версию прописывать в зависимостях нужно явно). Разве что если библиотека качается напрямую из репозитория без привязки к тегу/коммиту, то при перезагрузки зависимостей будет взята самая свежая версия из репозитрия.

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

Полагаю, что на ваш сценарий, когда вы вносите правки в libA и libB прямо в рабочей копии appA, это не ляжет. Но это не самый лучший сценарий, имхо.

Большие зависимости вроде Boost или ACE подтягивать пока не приходилось, но подтянуть SOCI, rapidjson, catch, fmtlib, spdlog, procxx и еще десятка полтора подобного размера библиотек в проект получается запросто.

При этом механизм получился не привязанный к build-системам. Т.е. качать зависимости можно с помощью MxxRu::externals, а собирать затем все — через CMake или через что-то еще. Ну а на Ruby делали потому, что так было проще и быстре.

Маленький пример:

MxxRu::arch_externals :libmosquitto do |e|
  e.url 'https://github.com/eclipse/mosquitto/archive/v1.4.8.zip'

  e.map_dir 'lib' => 'dev/libmosquitto'
  e.map_file 'config.h' => 'dev/libmosquitto/*'
end

MxxRu::arch_externals :fmt do |e|
  e.url 'https://github.com/fmtlib/fmt/archive/3.0.0.zip'

  e.map_dir 'cppformat' => 'dev/fmt'
  e.map_dir 'fmt' => 'dev/fmt'
end

MxxRu::arch_externals :spdlog do |e|
  e.url 'https://github.com/gabime/spdlog/archive/v0.9.0.zip'

  e.map_dir 'include' => 'dev/spdlog'
end

MxxRu::arch_externals :catch do |e|
  e.url 'https://github.com/philsquared/Catch/archive/v1.5.6.tar.gz'

  e.map_file 'single_include/catch.hpp' => 'dev/catch/*'
end

MxxRu::git_externals :rapidjson do |e|
  e.url 'https://github.com/miloyip/rapidjson.git'
  e.commit '2a3fbdaf5c6991124dacbd9fcdf709f9b044ffe5'
  e.map_dir 'include/rapidjson' => 'dev/rapidjson/include'
end

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)

Submodule - это однозначно ад. Subtree точно лучше submodule, но со своими тараканами. Ну и монореп.

Я у себя пока просто прокинул групповые команды в Makefile из корневой папки (make pull, make push, make status). Костыльно конечно, но пока хватает кое-как. Сам бы хотел что-нибудь получше.

Vit ★★★★★
()

Что на счёт Google Repo, который используется в разработке Android?

Dendy ★★★★★
()
Ответ на: комментарий от eao197

Спасибо за ссылки на проект. Я обязательно на него посмотрю. Выглядит довольно интересно. Из минуса для меня лично — это рубевый проект. Но это можно пережить.

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

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

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

Все больше смотрю на гугловые инструменты. Как я понял, у них для андроида используется утилита repo (http://source.android.com/source/using-repo.html). Так выглядит для нее манифест, хранящийся тоже в гите: https://android.googlesource.com/platform/manifest/ /master/default.xml . Она практически идеальна:

  • Написана на питоне (его я тоже не особо знаю, на его синтаксис понятнее чем ruby. При отсутствии вменяемой документации чтение исходников большой плюс.)
  • как я понял из описания, умеет централизованно создавать в каждом из проектов новые бранчи, ребайзить их на мастер. Есть возможность вызвать git diff в каждом проекте.
  • есть возможноть выполнить repo forall -c <COMMAND> с кастомной командой для всех проектов. Может пригодиться.

В общем, все супер. За исключением существенного минуса: у них в repo не хранятся конкретные версии сборок. можно указывать имя бранча/тега и все. Я не знаю как они работают, запускают повторные сборки.

В хроме используется gclient. Он выглядит посложнее, но, судя по всему, позволяет хранить версии ревизий. Нужно будет смотреть его.

Deleted
()
Ответ на: комментарий от Vit

Submodule - это однозначно ад. Subtree точно лучше submodule

надеюсь, ты в курсе что submodule и subtree - это 2 отдельных инструмента, решающих 2 разные задачи, и поэтому один никак не может быть лучше/хуже другого, в общем смысле? только в рамках какой-то определенной решаемой задачи.

а то у тебя это как-то слишком категорично написано, как будто тебя заставили использовать submodule, когда был нужен subtree.

waker ★★★★★
()
Ответ на: комментарий от waker

В курсе естессна. Submodule катит для подтягивания зависимостей, но не для одновременной разработки. Я их рядом поставил, потому что когда петух клюнет, все начинают искать хоть что-нибудь, а кроме submodule/subtree на эту тему больше и нет ничего.

С одновременной разработкой в git до сих пор тухло, по большому счету.

Vit ★★★★★
()
Ответ на: комментарий от Deleted

1) При этом теряется история изначальных проектов.

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

2) git log на таком проекте будет выдавать ад

git log -- path/to/libB выдаст историю только для libB, без всего окружающего.

ilammy ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.