Олег Марков
Добавление подмодуля в Git - команда git submodule add
Введение
В реальных проектах вам часто нужно использовать код из другого репозитория: общую библиотеку, модуль авторизации, UI-компоненты или, например, документацию. Один из способов аккуратно подключить такой внешний код — использовать подмодули Git.
Команда git submodule add позволяет добавить один Git‑репозиторий внутрь другого так, чтобы:
- основной репозиторий «знал», какую именно версию внешнего репозитория вы используете;
- можно было обновлять этот внешний модуль и фиксировать конкретные версии;
- другие разработчики могли легко клонировать проект вместе с подмодулями.
Давайте разберем, как это работает на практике, какие есть подводные камни и как с ними справляться.
Что такое подмодуль в Git и когда он нужен
Общее представление
Смотрите, подмодуль — это по сути «ссылка» на другой репозиторий внутри вашего проекта. В файловой системе вы видите обычную папку, но Git хранит в родительском репозитории не содержимое этой папки, а только:
- путь к подмодулю;
- URL внешнего репозитория;
- конкретный коммит, на который указывает подмодуль.
Это значит, что:
- Подмодуль всегда «прибит» к конкретному коммиту внешнего репозитория, а не к ветке в целом.
- Чтобы обновить подмодуль до свежей версии, нужно отдельно перейти внутрь подмодуля, подтянуть изменения и зафиксировать новый коммит подмодуля в основном репозитории.
Когда использовать подмодули
Подмодули хорошо подходят, если:
У вас есть общая библиотека, которую используют несколько проектов, и вы хотите:
- хранить ее отдельно;
- но при этом фиксировать в каждом проекте конкретную версию этой библиотеки.
Вы не хотите копировать внешний код внутрь проекта:
- чтобы не дублировать историю;
- чтобы можно было вести развитие этого кода отдельно.
Вы хотите контролируемо обновлять зависимости:
- не по последнему коммиту в ветке master/main;
- а «поднимать» версию только когда вы готовы.
Но подмодули усложняют рабочий процесс, поэтому лучше использовать их там, где действительно нужно:
- библиотечные репозитории;
- общие инфраструктурные модули (CI‑скрипты, деплой‑конфигурации, шаблоны);
- большие независимые компоненты (например, фронтенд и бэкенд как отдельные репозитории, но собранные в одном).
Теперь давайте перейдем к основной команде — git submodule add.
Базовый синтаксис команды git submodule add
Общий формат
Команда имеет несколько вариантов, но базовый синтаксис такой:
git submodule add <репозиторий> [путь]
# <репозиторий> — URL или относительный путь до репозитория подмодуля
# [путь] — путь в текущем репозитории, куда будет помещен подмодуль
Если путь не указать, Git возьмет имя репозитория из URL и создаст папку с этим именем.
Например, вы хотите добавить общую библиотеку в папку libs/common-lib:
git submodule add https://github.com/example/common-lib.git libs/common-lib
# common-lib.git — это репозиторий подмодуля
# libs/common-lib — папка в текущем проекте, где появится подмодуль
После этой команды происходит несколько вещей:
- В корне проекта создается (или обновляется) файл
.gitmodules. - В указанный путь клонируется внешний репозиторий.
- Основной репозиторий фиксирует ссылку на конкретный коммит подмодуля.
Как работает файл .gitmodules
Структура файла
Файл .gitmodules — это конфигурационный файл, где Git хранит информацию обо всех подмодулях проекта. Смотрите, как он выглядит:
[submodule "libs/common-lib"]
path = libs/common-lib # Путь к подмодулю в вашем репозитории
url = https://github.com/example/common-lib.git # URL исходного репозитория
Каждый подмодуль описан отдельным блоком submodule "<имя>".
Важно:
.gitmodulesхранится в репозитории, его нужно добавлять в коммит.- Если этого не сделать, другие участники не узнают о подмодуле.
Что нужно закоммитить после git submodule add
Давайте разберем типичную последовательность действий:
git submodule add https://github.com/example/common-lib.git libs/common-lib
git status
# Здесь вы увидите:
# - новый файл .gitmodules
# - новую «папку» libs/common-lib (для Git это специальный тип — gitlink)
git add .gitmodules libs/common-lib
git commit -m "Добавлен подмодуль common-lib"
Комментарий:
.gitmodulesописывает конфиг подмодуля.- Папка
libs/common-libв индексе будет храниться как ссылка на конкретный коммит внешнего репозитория, а не как обычная папка с файлами.
Примеры добавления подмодулей
Пример 1. Добавление публичного репозитория с GitHub
Представим, что у вас есть основной проект, и вы хотите подключить внешний модуль:
# Переходим в корень основного проекта
cd /path/to/project
# Добавляем подмодуль из GitHub
git submodule add https://github.com/example/logger.git libs/logger
# logger.git — внешний репозиторий
# libs/logger — папка внутри проекта, куда попадет код подмодуля
После выполнения команды вы увидите:
- в папке
libs/logger— файлы подмодуля; - в корне — файл
.gitmodules; - в
git status— изменения и для.gitmodules, и для самого подмодуля.
Дальше:
git add .gitmodules libs/logger # Добавляем конфиг и ссылку на подмодуль
git commit -m "Подключен подмодуль logger"
Пример 2. Добавление подмодуля по SSH
Если вы используете SSH‑доступ к Git (часто в приватных репозиториях), можно указать SSH‑URL:
git submodule add git@github.com:example/logger.git libs/logger
# Используем SSH-формат URL
Важно, чтобы у всех разработчиков, кто будет работать с этим проектом, был доступ по SSH к этому репозиторию. Иначе они не смогут инициализировать подмодуль.
Пример 3. Добавление локального репозитория как подмодуля
Если у вас есть локальный репозиторий (например, в соседней папке), можно использовать относительный путь:
# Допустим, у вас есть:
# /projects/app — основной проект
# /projects/libs/auth — отдельный репозиторий с модулем авторизации
cd /projects/app
git submodule add ../libs/auth services/auth
# ../libs/auth — путь до локального репозитория
# services/auth — папка, куда поместится подмодуль
Git в .gitmodules все равно сохранит URL (часто перепишет его в относительный путь или в тот, что потом используется по сети).
Дополнительные параметры git submodule add
Команда git submodule add поддерживает ряд опций. Давайте разберем ключевые, которые реально полезны на практике.
Параметр -b / --branch — фиксация ветки по умолчанию
По умолчанию подмодуль указывает на конкретный коммит, а не на ветку. Но при этом в .gitmodules можно сохранить информацию о том, какая ветка подмодуля должна использоваться как «основная» при обновлениях или при работе некоторых команд.
Синтаксис:
git submodule add -b main https://github.com/example/common-lib.git libs/common-lib
# -b main — говорим Git, что подмодуль ориентируется на ветку main
Обратите внимание:
- Git все равно зафиксирует конкретный коммит.
- Но в
.gitmodulesбудет записано:
[submodule "libs/common-lib"]
path = libs/common-lib
url = https://github.com/example/common-lib.git
branch = main # Указана ветка по умолчанию
Это помогает, если вы планируете регулярно обновлять подмодуль по конкретной ветке.
Параметр --name — ручное имя подмодуля в .gitmodules
По умолчанию Git берет имя подмодуля из пути. Например:
git submodule add https://github.com/example/logger.git libs/logger
В .gitmodules появится:
[submodule "libs/logger"]
path = libs/logger
url = https://github.com/example/logger.git
Если вы хотите указать другое логическое имя (например, без вложенных путей), можно использовать:
git submodule add --name logger-lib https://github.com/example/logger.git libs/logger
Тогда:
[submodule "logger-lib"]
path = libs/logger
url = https://github.com/example/logger.git
Такое бывает удобно, если:
- путь может со временем меняться;
- но вы хотите сохранить логическое имя подмодуля.
Параметр --force — перезапись существующего пути
Если в целевом пути уже есть данные (например, вы раньше клонировали репозиторий вручную), Git не даст добавить туда подмодуль без явного подтверждения.
Пример:
git submodule add https://github.com/example/logger.git libs/logger
# Если в libs/logger уже есть папка, вы получите ошибку
В этом случае вы можете:
- либо удалить папку вручную;
- либо явно указать, что хотите перезаписать:
git submodule add --force https://github.com/example/logger.git libs/logger
# --force скажет Git игнорировать существующее содержимое
Используйте это осторожно, чтобы случайно не потерять нужные файлы.
Что происходит внутри Git при добавлении подмодуля
Особый тип объекта — gitlink
Когда вы добавляете подмодуль, Git не хранит все его файлы в коммите основного репозитория. Вместо этого в индексе и коммите появляется специальный тип записи — gitlink.
Упрощенно:
- Для обычных файлов Git хранит blob‑объекты (содержимое файлов).
- Для подмодуля Git хранит ссылку вида: «в этой папке лежит репозиторий, и он сейчас на коммите X».
Это видно по типу в некоторых командах (например, в git ls-tree).
Почему подмодуль не двигается сам при обновлении внешнего репозитория
Поскольку основному репозиторию сохранен конкретный SHA‑хеш коммита подмодуля, это жесткая фиксация.
Если в самом внешнем репозитории появятся новые коммиты, ваш основной проект:
- ничего об этом не «узнает» автоматически;
- будет продолжать ссылаться на старый коммит, пока вы вручную не обновите подмодуль и не зафиксируете это изменение.
Такой подход дает воспроизводимость сборок: вы всегда можете собрать проект с точно теми версиями, которые были когда-то зафиксированы.
Клонирование репозитория с подмодулями
Вариант 1. Сначала clone, потом init и update
Если вы просто клонируете репозиторий с подмодулями:
git clone https://github.com/example/app.git
cd app
Папки подмодулей появятся, но будут пустыми (или в виде специальных записей). Чтобы загрузить их содержимое, нужно выполнить:
git submodule init
# Регистрирует подмодули на основе файла .gitmodules
git submodule update
# Клонирует подмодули и переключает их на нужные коммиты
Если подмодулей несколько и они вложенные, удобно использовать:
git submodule update --init --recursive
# --init — инициализирует
# --recursive — обходит вложенные подмодули
Вариант 2. Клонирование сразу с подмодулями
Чтобы не выполнять дополнительные команды, можно при клонировании указать:
git clone --recurse-submodules https://github.com/example/app.git
# --recurse-submodules выполняет init + update для всех подмодулей
Здесь вы сразу получите:
- основной репозиторий;
- инициализированные подмодули с нужными версиями.
Работа с подмодулями после добавления
Переход внутрь подмодуля и обычная работа
Смотрите, подмодуль — это полноценный Git‑репозиторий, просто вложенный в другой. Поэтому все операции внутри него стандартные.
cd libs/common-lib
# Проверяем текущие ветки и состояние
git status
git branch
# Забираем обновления из удаленного репозитория
git fetch
# Переключаемся, например, на ветку main
git checkout main
Подмодуль ведет свою историю независимо от основного проекта.
Обновление версии подмодуля в основном репозитории
Давайте разберем, как обновить подмодуль до более свежей версии.
Переходим в подмодуль:
cd libs/common-libПолучаем изменения и обновляемся, например, до последнего коммита в ветке main:
git fetch git checkout main git pull origin main # Здесь вы обновляете сам подмодуль как обычный репозиторийВозвращаемся в корень основного проекта:
cd ../.. # Возврат в корень, путь зависит от вашей структурыПроверяем изменения:
git status # Вы увидите, что для подмодуля libs/common-lib сменился коммитФиксируем это изменение:
git add libs/common-lib git commit -m "Обновлена версия подмодуля common-lib"
Комментарий:
- В основном репозитории изменился только «хеш» подмодуля (ссылка на новый коммит).
- Файлы подмодуля как таковые в основной истории не записываются.
Обновление всех подмодулей до последних коммитов веток
Если вы хотите обновить сразу все подмодули (например, до их текущих origin/HEAD), можно использовать:
git submodule update --remote
# Обновляет подмодули до последних коммитов отслеживаемых веток (из .gitmodules)
Если там указана ветка (через параметр branch в .gitmodules), Git ориентируется на нее.
Например, если в .gitmodules:
[submodule "libs/common-lib"]
path = libs/common-lib
url = https://github.com/example/common-lib.git
branch = main
Тогда git submodule update --remote подтянет последний коммит из main.
Не забудьте потом закоммитить обновления:
git status
git add libs/common-lib # и другие подмодули, если нужно
git commit -m "Обновлены подмодули до актуальных версий"
Изменения в подмодуле и их публикация
Если вы разрабатываете сам подмодуль
Иногда подмодуль — это ваш же репозиторий, и вы хотите в нем что-то изменить.
Последовательность действий:
Переходите в папку подмодуля:
cd libs/common-libВносите изменения в код, коммитите их:
# Внесли изменения в файлы... git status git add . git commit -m "Добавлена новая функция логгера" git push origin main # Публикуете изменения в удаленный репозиторий подмодуляВозвращаетесь в основной репозиторий и фиксируете новый коммит подмодуля:
cd ../.. git status # Git покажет, что подмодуль libs/common-lib изменился (новый хеш) git add libs/common-lib git commit -m "Подмодуль common-lib обновлен до нового коммита"
Здесь важно не забывать:
- сначала запушить изменения из подмодуля;
- потом закоммитить ссылку на новый коммит в основном репозитории.
Иначе другие разработчики не смогут получить эти изменения, даже если обновят основной проект.
Если у вас нет прав записи в подмодуль
Если подмодуль — это внешний репозиторий, к которому у вас нет прав записи, вы можете:
- форкнуть репозиторий;
- использовать свой форк как подмодуль;
- или предложить изменения через pull request в исходный репозиторий.
В таком случае URL подмодуля в .gitmodules может указывать, например, на ваш форк.
Изменение и переименование подмодулей
Изменение URL подмодуля
Представьте, что репозиторий перенесли, либо вы переключились с HTTPS на SSH. Менять URL подмодуля лучше через команды Git.
Изменяем URL в конфигурации:
git submodule set-url libs/common-lib git@github.com:example/common-lib.git # libs/common-lib — путь к подмодулю # git@github.com:... — новый URLGit обновит:
- файл
.gitmodules; - локальную конфигурацию
.git/config.
- файл
Фиксируем изменения:
git add .gitmodules git commit -m "Обновлен URL подмодуля common-lib"
Перемещение подмодуля в другую папку
Если вам нужно перенести подмодуль в другую директорию, можно сделать это через Git, но последовательность немного сложнее, чем просто mv.
Подробнее:
Останавливаем отслеживание текущего пути подмодуля:
git rm --cached libs/common-lib # --cached — удаляет запись о подмодуле из индекса, не трогая файлыПеремещаем папку руками:
mv libs/common-lib modules/common-lib # Переносим каталог подмодуля на новый путьОбновляем конфиг
.gitmodules:# Можно отредактировать файл вручную # Было: # path = libs/common-lib # Стало: # path = modules/common-libСообщаем Git, что подмодуль теперь на новом пути:
git add .gitmodules git add modules/common-lib git commit -m "Перемещен подмодуль common-lib в modules/common-lib"
Обратите внимание, что здесь важно не удалять физические файлы подмодуля, а корректно обновить конфигурацию и индекс.
Удаление подмодуля из проекта
Удаление подмодуля — частый источник путаницы, поэтому давайте сделаем это шаг за шагом.
Предположим, у вас есть подмодуль в libs/common-lib.
Удаляем запись о подмодуле из индекса основного репозитория:
git rm --cached libs/common-lib # --cached — Git перестает отслеживать подмодуль на этом путиУдаляем блок про подмодуль из
.gitmodules:# Открываем .gitmodules и удаляем соответствующий блок: # [submodule "libs/common-lib"] # path = libs/common-lib # url = ...Опционально очищаем локальную конфигурацию Git:
git config -f .git/config --remove-section submodule.libs/common-lib 2>/dev/null # Удаляем локальную секцию подмодуля, если она естьУдаляем физические файлы подмодуля (по желанию):
rm -rf libs/common-lib # Удаляем папку подмодуля из файловой системыФиксируем изменения:
git add .gitmodules git commit -m "Удален подмодуль common-lib"
Если вы хотите полностью очиститься, убедитесь, что в репозитории нет следов подмодуля ни в .gitmodules, ни в .git/config, ни в индексе.
Типичные проблемы и подводные камни при использовании git submodule add
Подмодуль добавлен, но не закоммичен
Иногда разработчик выполняет:
git submodule add https://github.com/example/common-lib.git libs/common-lib
Но потом забывает добавить .gitmodules и сам путь подмодуля в коммит. В результате:
- у других разработчиков подмодуль не инициализируется;
- файл
.gitmodulesможет отсутствовать или быть неполным.
Проверка:
git status
# Убедитесь, что:
# - .gitmodules добавлен
# - путь к подмодулю добавлен
Решение:
git add .gitmodules libs/common-lib
git commit -m "Корректно добавлен подмодуль common-lib"
«Detached HEAD» в подмодуле
Когда вы клонируете проект с подмодулями, подмодуль часто оказывается в состоянии «detached HEAD» — то есть вы находитесь на конкретном коммите, не привязанном к ветке.
Проверить:
cd libs/common-lib
git status
# Git может показать, что вы не на ветке, а просто на коммите
Это нормально для подмодуля, если вы просто используете его как зависимость. Но если вы хотите там разрабатывать:
git checkout main
# или другую нужную ветку
Комментарий:
- Если вы сделаете коммиты в detached HEAD и не привяжете их к ветке, вы рискуете потерять эти изменения.
Несоответствие версий подмодуля у разных разработчиков
Бывает, что один разработчик обновил подмодуль, но не закоммитил изменение ссылки на новый коммит в основном репозитории. Тогда:
- у него локально подмодуль на новом коммите;
- у других — на старом;
- чекаут основного репозитория не двигает подмодуль.
Решение:
Разработчик, который обновлял подмодуль, должен:
git add путь/к/подмодулю git commit -m "Обновлен подмодуль ..." git pushОстальные выполняют:
git pull git submodule update --init --recursive
Конфликты в .gitmodules
Если несколько людей редактировали .gitmodules (например, меняли URL или добавляли подмодули параллельно), возможны конфликты.
Решение:
- Открыть
.gitmodules, аккуратно разрешить конфликт. Убедиться, что для каждого подмодуля:
- корректный
path; - правильный
url; - при необходимости —
branch.
- корректный
Сохранить файл и выполнить:
git add .gitmodules git commit
Рекомендации по использованию git submodule add в реальных проектах
Когда подмодуль — хороший выбор
Подмодуль стоит использовать, если:
- вы хотите разделить код на независимые репозитории;
- у вас есть переиспользуемая библиотека;
- вам важна строгая фиксация версий и воспроизводимость сборок.
Пример:
- общий модуль логирования;
- общие компоненты интерфейса для нескольких приложений;
- инфраструктурный репозиторий с CI‑скриптами.
Когда лучше рассмотреть альтернативы
Подумаете, нужен ли вам действительно git submodule add, если:
- зависимость — это библиотека, доступная через пакетный менеджер (Go modules, npm, Maven, Composer и т.д.);
- достаточно указать версию в файле зависимостей, а не тянуть весь Git‑репозиторий как подмодуль.
Также есть альтернативы:
- git subtree — когда вы хотите включить внешний репозиторий в историю основного;
- копирование кода (в редких случаях).
Организационные моменты
Чтобы подмодули не превратились в хаос, полезно:
- использовать понятные пути (например,
libs/имяилиmodules/имя); - договориться в команде, кто и когда обновляет подмодули;
описать в README проекта инструкции:
- как клонировать репозиторий с подмодулями;
- какие команды запускать для обновления.
Пример блока в README:
# Клонирование проекта вместе с подмодулями
git clone --recurse-submodules <url>
# Если проект уже склонирован
git submodule update --init --recursive
# Обновление подмодулей до актуальных версий
git submodule update --remote --recursive
Это сильно снижает количество вопросов у новых участников команды.
Подмодули в Git — мощный инструмент, который позволяет гибко организовывать код в нескольких репозиториях, но требует аккуратного обращения. Команда git submodule add — точка входа в эту функциональность. Если вы будете:
- всегда коммитить
.gitmodulesи ссылки на подмодули; - внимательно относиться к обновлениям и состоянию подмодулей;
- документировать правила в команде;
то подмодули станут удобным и предсказуемым инструментом, а не источником постоянных проблем.
Частозадаваемые технические вопросы
Вопрос 1. Как сделать так, чтобы подмодуль всегда следовал за веткой main без ручного обновления?
Полностью автоматизировать это в чистом Git нельзя — подмодуль по определению фиксируется на коммите. Но можно упростить процесс:
В
.gitmodulesукажите ветку:[submodule "libs/common-lib"] path = libs/common-lib url = https://github.com/example/common-lib.git branch = mainДля обновления используйте:
git submodule update --remote libs/common-libЗакоммитьте обновление подмодуля в основной репозиторий.
Вопрос 2. Как добавить подмодуль, но использовать относительный URL, чтобы репозитории работали и на разных серверах?
Можно сразу задать относительный URL:
git submodule add ../common-lib.git libs/common-lib
# Относительный путь к репозиторию относительно текущего
Git сохранит его в .gitmodules. Это удобно, если основной репозиторий и подмодуль лежат рядом в структуре Git‑сервера и мигрируют вместе.
Вопрос 3. Как сделать вложенные подмодули (подмодуль внутри подмодуля) и не запутаться?
Последовательность:
- Внутри подмодуля выполняете git submodule add как обычно.
В корне основного репозитория при клонировании и обновлении всегда используйте:
git submodule update --init --recursive # --recursive захватывает вложенные подмодулиОбязательно коммитьте
.gitmodulesи изменения ссылок на подмодули на каждом уровне.
Вопрос 4. Как временно изменить URL подмодуля только для себя (например, на свой форк)?
Используйте локальную конфигурацию:
git config submodule.libs/common-lib.url git@github.com:myfork/common-lib.git
# Меняется только в .git/config, не в .gitmodules
Так вы измените источник подмодуля только локально, не затрагивая остальных участников.
Вопрос 5. Как восстановить сломанный подмодуль, если я случайно удалил его папку вручную?
Убедитесь, что запись о подмодуле осталась в
.gitmodulesи в индексе:git statusВосстановите содержимое подмодуля:
git submodule update --init путь/к/подмодулю # Git заново клонирует и переключит подмодуль на нужный коммит
Если .gitmodules был поврежден, восстановите его из истории:
git checkout HEAD -- .gitmodules
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев