Пошук уроків, статей та іншого контенту
Підключаємо зовнішні репозиторії через submodules, оновлюємо їх і керуємо версіями залежностей.
Submodule — це репозиторій Git, вбудований в інший репозиторій як піддиректорія.
Основний репозиторій зберігає не всі файли submodule, а лише:
шлях до вкладеного репозиторію;
URL віддаленого репозиторію в .gitmodules;
конкретний commit, на який посилається submodule.
Тому submodule дає змогу підключити зовнішній проєкт і зафіксувати його точну версію.
Наприклад, структура проєкту може виглядати так:
my-application/
├── .git/
├── .gitmodules
├── src/
└── vendor/
└── shared-library/shared-library є окремим Git-репозиторієм зі власною історією комітів.
Submodule додають командою:
git submodule add <repository-url> <path>Наприклад:
git submodule add https://github.com/example/shared-library.git vendor/shared-libraryПісля цього Git:
клонуватиме зовнішній репозиторій у vendor/shared-library;
створить або оновить файл .gitmodules;
додасть submodule до індексу основного репозиторію.
Файл .gitmodules матиме приблизно такий вигляд:
[submodule "vendor/shared-library"]
path = vendor/shared-library
url = https://github.com/example/shared-library.gitПеревірити стан можна командою:
git statusОсновний репозиторій покаже submodule як змінений елемент. Зафіксуйте зміни звичайним комітом:
git add .gitmodules vendor/shared-library
git commit -m "Add shared library submodule"Важливо: коміт у батьківському репозиторії фіксує конкретний commit submodule, а не його поточну гілку чи останню версію.
Якщо клонувати основний репозиторій звичайним способом:
git clone https://github.com/example/my-application.git
cd my-applicationкаталог submodule може бути порожнім або неініціалізованим.
Щоб одразу клонувати всі submodules, використовуйте:
git clone --recurse-submodules https://github.com/example/my-application.gitОпція --recurse-submodules також ініціалізує вкладені submodules, якщо вони є.
Якщо репозиторій уже клоновано без цієї опції, виконайте:
git submodule update --init --recursiveТут:
--init налаштовує submodules, описані у .gitmodules;
update завантажує commit, який очікує основний репозиторій;
--recursive повторює операцію для вкладених submodules.
Типовий процес підготовки вже клонованого проєкту:
git clone https://github.com/example/my-application.git
cd my-application
git submodule update --init --recursiveЩоб переглянути commit кожного submodule:
git submodule statusПриклад результату:
a1b2c3d4e5f6 vendor/shared-libraryСимвол перед commit має спеціальне значення:
пробіл — submodule перебуває на очікуваному commit;
- — submodule ще не ініціалізовано;
+ — submodule перебуває на іншому commit, ніж записано в основному репозиторії;
U — є конфлікт оновлення.
Детальніший стан можна отримати за допомогою:
git submodule foreach 'git status --short'Команда виконає git status --short у кожному submodule.
Коли ви перемикаєте гілку основного репозиторію, Git може змінити очікуваний commit submodule.
Щоб привести submodules до версій, записаних у поточному commit основного репозиторію, виконайте:
git submodule update --init --recursiveЦя команда не завантажує довільну найновішу версію. Вона встановлює саме ті commits, які зафіксовані в основному репозиторії.
Це одна з головних переваг submodules: різні розробники та середовища використовують однакові версії залежностей.
Щоб оновити submodule до commit із віддаленого репозиторію, спочатку перейдіть у його каталог:
cd vendor/shared-library
git fetch origin
git checkout main
git pull origin mainПісля цього поверніться до основного репозиторію:
cd ../..
git statusGit покаже, що submodule змінився. Основний репозиторій тепер бачить новий commit submodule, але ще не зберіг його у власній історії.
Зафіксуйте нову версію:
git add vendor/shared-library
git commit -m "Update shared library"У результаті основний репозиторій почне посилатися на новий commit submodule.
git submodule update --remoteДля оновлення submodule до останнього commit його віддаленої гілки можна використати:
git submodule update --remote vendor/shared-libraryЗа замовчуванням Git використовує віддалену гілку, налаштовану для submodule. Яку саме гілку потрібно відстежувати, можна вказати у .gitmodules:
[submodule "vendor/shared-library"]
path = vendor/shared-library
url = https://github.com/example/shared-library.git
branch = mainПісля зміни .gitmodules синхронізуйте локальну конфігурацію:
git submodule sync --recursiveПотім оновіть submodule:
git submodule update --remote --merge vendor/shared-libraryОпція --merge намагається злити нові зміни з поточною локальною гілкою submodule. Без неї submodule зазвичай буде переміщено безпосередньо на потрібний commit, часто в режим detached HEAD.
Після оновлення не забудьте зафіксувати посилання на новий commit у головному репозиторії:
git add .gitmodules vendor/shared-library
git commit -m "Update shared library to latest main"Submodule є повноцінним Git-репозиторієм. У ньому можна створювати коміти, перемикати гілки та виконувати звичайні Git-команди.
cd vendor/shared-library
git switch -c improve-validation
# Змініть файли бібліотеки
git add .
git commit -m "Improve validation"
git push -u origin improve-validationПісля цього основний репозиторій бачить submodule на новому commit:
cd ../..
git statusАле сам commit у submodule та посилання на нього в основному репозиторії — це дві різні операції:
# У репозиторії submodule
git push
# У батьківському репозиторії
git add vendor/shared-library
git commit -m "Use improved validation"
git pushСпочатку commit submodule має бути доступним у його віддаленому репозиторії. Інакше інші розробники не зможуть завантажити commit, на який посилається основний репозиторій.
Після git submodule update submodule часто перебуває у стані detached HEAD.
Це означає, що HEAD вказує безпосередньо на commit, а не на локальну гілку:
cd vendor/shared-library
git statusМожливий результат:
HEAD detached at a1b2c3dДля використання submodule як залежності це нормально. Основний репозиторій якраз має вказувати на конкретний commit.
Якщо потрібно розробляти код самого submodule, переключіться на гілку:
git switch main
git pull origin mainАбо створіть власну гілку:
git switch -c fix/incorrect-resultНе варто створювати коміти в detached HEAD, якщо ви не плануєте одразу зберегти їх у гілці. Такий commit може залишитися недоступним після перемикання на інший стан.
URL submodule можна змінити командою:
git config -f .gitmodules submodule.vendor/shared-library.url https://github.com/example/new-shared-library.git
git submodule sync --recursiveПісля цього зафіксуйте зміну:
git add .gitmodules
git commit -m "Change shared library repository URL"Команда git submodule sync оновлює локальну конфігурацію submodules відповідно до .gitmodules.
Якщо URL потрібно змінити лише локально, без зміни .gitmodules, використовуйте:
git config submodule.vendor/shared-library.url https://github.com/example/new-shared-library.gitПроте інші учасники проєкту не отримають таку локальну зміну після git pull.
Для видалення submodule потрібні кілька кроків:
git submodule deinit -f vendor/shared-library
git rm -f vendor/shared-library
rm -rf .git/modules/vendor/shared-library
git commit -m "Remove shared library submodule"Що роблять ці команди:
git submodule deinit -f видаляє локальну конфігурацію submodule;
git rm -f видаляє каталог і запис із .gitmodules;
rm -rf .git/modules/vendor/shared-library видаляє внутрішні Git-дані submodule;
останній git commit зберігає видалення в основному репозиторії.
Перед видаленням переконайтеся, що у submodule немає незбережених змін або комітів, які ще потрібно опублікувати.
Нехай основний проєкт використовує бібліотеку shared-library.
Додавання залежності:
git submodule add https://github.com/example/shared-library.git vendor/shared-library
git add .gitmodules vendor/shared-library
git commit -m "Add shared library"Підготовка нового робочого середовища:
git clone --recurse-submodules https://github.com/example/my-application.git
cd my-applicationСинхронізація вже клонованого проєкту:
git pull
git submodule update --init --recursiveОновлення бібліотеки:
git submodule update --remote vendor/shared-library
git add vendor/shared-library
git commit -m "Update shared library"
git pushПісля останньої команди основний репозиторій зберігає новий commit залежності. Інші розробники отримають саме його після:
git pull
git submodule update --init --recursive.gitmodulesЯкщо виконати лише:
git add vendor/shared-libraryале не додати .gitmodules, інші користувачі можуть не знати URL, з якого потрібно отримати submodule.
Завжди перевіряйте:
git status
git diff --cachedПеред комітом мають бути додані і .gitmodules, і шлях submodule.
git pull оновить код submoduleЗвичайний:
git pullоновлює основний репозиторій і змінює commit, на який він посилається. Файли submodule потрібно синхронізувати окремо:
git submodule update --init --recursiveПісля переходу submodule на інший commit основний репозиторій бачить зміну, але ще не зберігає її.
Потрібно виконати:
git add path/to/submodule
git commit -m "Update submodule"Якщо submodule перебуває у detached HEAD, створений commit не належить локальній гілці. Перед розробкою переключіться на гілку:
git switch mainабо створіть нову:
git switch -c feature/my-changeОсновний репозиторій може посилатися на будь-який локальний commit submodule. Але якщо цей commit не відправлено у віддалений репозиторій, інші не зможуть виконати:
git submodule updateСпочатку опублікуйте commit у submodule:
cd path/to/submodule
git pushі лише потім фіксуйте оновлення в основному репозиторії.
--recursiveЯкщо submodule містить інші submodules, команда без --recursive ініціалізує лише верхній рівень.
Для повного оновлення використовуйте:
git submodule update --init --recursiveSubmodule підключає один Git-репозиторій до іншого.
Основний репозиторій зберігає commit submodule, а не всі його файли.
.gitmodules містить шлях і URL кожного submodule.
Для клонування разом із залежностями використовуйте git clone --recurse-submodules.
Для вже клонованого репозиторію використовуйте git submodule update --init --recursive.
Оновлення submodule потрібно завершити комітом у батьківському репозиторії.
Commit submodule має бути доступним у його віддаленому репозиторії.
Detached HEAD у submodule нормальний під час використання конкретної зафіксованої версії.
Після оновлення .gitmodules запускайте git submodule sync --recursive.