Подмодульные библиотеки в git для минимизации избыточности
Я очень новичок в использовании git и ранее не пытался "организовать" проекты, над которыми я работал. Однако я недавно приобрел сервер разработки для личного использования и хотел начать организовывать все свои проекты и использовать контроль версий.
Последние 8 часов я провел, исследуя различные рекомендуемые методы организации файлов в проекте, и я понимаю, что это очень субъективный вопрос. Однако я разработал систему, которая, я думаю, будет работать практически для любой цели, и у меня есть один очень объективный вопрос относительно того, как выполнить определенную задачу с помощью структуры каталогов.
В настоящее время я смотрю на структуру, похожую на следующее:
src/ - All deliverables in an uncompiled form (PHP files, c source files, etc)
data/ - Crucial but unrelated data (SQL databases, etc.)
lib/ - Dependencies -- THIS IS WHERE MY QUESTION LIES
docs/ - Documentation
build/ - Scripts to aide in the build process
test/ - Unit tests
res/ - Not version controlled. Contains PSD files and non-diff-able stuff
.gitignore
README
output.zip - Ready-to-install finished product (just unzip and go)
Как я уже говорил, моя настоящая проблема вращается вокруг этого lib/
каталог. Это должно содержать все файлы и программы, которые требуются моему проекту для запуска, но которые находятся за пределами моего проекта, и я не буду редактировать. Некоторые функции, которые мне нужны для этой папки:
- Так как они необходимы для запуска моего конечного продукта, они должны быть включены в output.zip
- Я хотел бы, чтобы эта папка контролировалась версией, чтобы любой, кто загружает мой git-репозиторий, имел доступ ко всем зависимостям
- Если несколько проектов имеют одинаковую зависимость, я НЕ хочу иметь 18 избыточных копий одного и того же файла на моем сервере
- I would like to be able to pull these dependencies from other projects of mine (one project should be able to serve as a library for a separate project)
I can avoid having 18 redundant copies of the same file by using a virtual directory (symlink), however from my understanding git would copy this symlink as-is into the repository without copying the files. Therefore if anyone else fetched my repository they would have a dangling pointer and no libraries.
At first it looked like I could do what I wanted using git-submodule. However from my understanding this takes the entire contents of another repository and treats it as a sub-directory. Therefore if I included "dependency A" my libraries folder would look something like:
/lib/A/src/
/lib/A/data/
...
/lib/A/test/
.gitignore
README
output.zip
In the case of a script (PHP, Perl, etc.) I could probably load the dependency using require('lib/A/src/dependency.php')
, but in the case of a DLL or binary file I would have no easy way to read the output file from output.zip. I could have the finished project stored directly at the root level instead of wrapped up in a pretty zip file, but if the project were, say, a website - this could mean hundreds of files cluttering up my repository root.
How can I include another repository as a library of my own, easily reference the library files within my own project, have the library meaningfully copied to anyone who fetches my repository, and prevent redundant copies of the same files on my development server?
EDIT: After searching on Google for a while I found this similar issue, however it only addresses PHP projects. While an autoloader may allow you to mask the underlying file system in a PHP environment, how would you apply a similar approach to a C++ project? Or a Python project? Or a Java project?
As I thought more about this project today a few other thoughts came to mind which may require a new direction of thought. First is the problem of very deep library nests. If project A depends on project B which depends on project C which depends on project D then you would have a directory structure like so:
A/lib/
A/lib/B/
A/lib/B/lib/
A/lib/B/lib/C/
A/lib/B/lib/C/lib/
A/lib/B/lib/C/lib/D/
Obviously this would not only get annoying, but redundant in its own way.
How do normal people deal with dependencies when doing a git repository?
4 ответа
В проектах, над которыми я работал, подмодули хороши только для определенных случаев, когда речь идет об управлении зависимостями, в других случаях это дополняется другой структурой. Чаще всего я предпочитаю использовать подмодули, когда мне нужен полный репозиторий, например, если у меня есть общий скрипт сборки, который я могу использовать в разных проектах.
Существуют специальные инструменты, ориентированные на управление зависимостями в различных стеках.
и т.п.
Эти инструменты заботятся об управлении резервированием.
В настоящее время я нахожусь в проекте.net, где у нас есть эта настройка -
- Скрипты сборки Powershell совместно используются проектами с помощью субмодулей. Репозиторий Buildscript содержит все исполняемые файлы третьих сторон, необходимые для развертывания любого из наших приложений.net и соответствующие сценарии оболочки PowerShell, а также некоторые сценарии для загрузки соглашений, конфигурации и т. Д.
- Сервер Nuget (через Teamcity), на котором размещены пакеты nuget для общих двоичных файлов, используемых в разных проектах. Восстановление пакета Nuget - это функция, которая позволяет получать пакеты как часть сборки.
Хотя приятно объединить рабочий процесс, вы должны уважать зверя, которого вы пытаетесь приручить. У вас должны быть разные структуры каталогов для разных проектов. Работая от 3D-анимационных проектов до PHP-проектов и C++-проектов, я обнаружил, что сжатие их для соответствия одному и тому же рабочему процессу просто добавляет работу и головную боль в долгосрочной перспективе. Большинство IDE имеют хорошую структуру "нового проекта" прямо из коробки, и это то, что другие разработчики сразу узнают и поймут.
Что касается проблемы зависимости, попробуйте реализовать суперпроектный подход: http://git-scm.com/book/en/Git-Tools-Submodules
Вы задали общий вопрос, но также задали вопрос о нескольких случаях. Я собираюсь склоняться к тому, чтобы быть более общим. Короткий ответ: это проблема системы сборки, а не система контроля версий.
В случае с Java есть несколько различных инструментов управления / разрешения зависимостей, которые вы можете использовать. Система сборки должна понимать, как извлечь эти зависимости во время сборки и сделать их доступными. Однако они временные - вы не регистрируете их для контроля версий. Кроме того, Maven, например, использует /target
папка, в которой содержатся ваши выходные данные (например, output.zip - что я бы также рекомендовал, поскольку это облегчает очистку выходных данных. Что делать, если у вас более одного выходного файла? Как насчет вариантов? и т. д.), а также другие элементы, например статические результаты анализа - и он также использует внешний каталог для локального кэширования зависимостей, но это может быть эфемерно, и это не будет заботиться. Итог: он не сохраняется в системе контроля версий.
Насколько я знаю, это не так просто в C++. Кажется, CMake поддерживает создание внешних проектов. Я только недавно начал играть с этим, чтобы увидеть, что возможно, поэтому я не хочу вводить вас в заблуждение, говоря: "это легко сделать", но само собой разумеется, что это можно сделать, вопрос в том, только сколько работы вы должны вложить в него. Так ли вы называете папку /libs
, вы должны заставить сборку обрабатывать зависимости как транзитивные (и тогда удачи с транзитивными зависимостями).
Не вставляйте библиотеки, это кошмар безопасности! Например, когда вы встраиваете в приложение некоторую библиотеку форматов изображений, такую как libpng, libjpeg или libtiff, потому что вы хотите использовать ее формат изображений, вы открываете свое приложение для любых уязвимостей безопасности, которые могут быть в этих библиотеках, и у пользователя нет простого способа узнать, что им нужно обновить вашу программу для решения проблемы безопасности. Когда вы оставляете зависимость вне области вашего приложения, тогда менеджер пакетов знает о библиотеке и может предпринять действия, когда обнаруживаются уязвимости безопасности.
Оставьте библиотеки, от которых вы зависите, за пределами вашего проекта. Если вы лично разработали библиотеки, которые используете в нескольких проектах, поместите их в свой собственный репозиторий и сделайте отдельные выпуски.
Для Unix-подобных ОС (linux/bsd/solaris/ и т. Д.) Пользователи должны устанавливать их отдельно через менеджер пакетов, если вы выпускаете ваше программное обеспечение, менеджер пакетов узнает о ваших зависимостях и установит необходимые зависимости до того, как установит ваше приложение, поэтому не требуется никакого руководства действия необходимы.
Для Windows используйте отдельный процесс связывания для объединения библиотек, от которых вы зависите, в удобный установщик, который устанавливает библиотеки в общие системные каталоги, а не в каталог вашей программы.
Кстати, в git нет технических средств, чтобы делать то, что вы хотите, без массового дублирования.