Олег Марков
Инициализация подмодулей в Git - git submodule init
Введение
Подмодули в Git помогают подключать один репозиторий внутрь другого как зависимость. Представьте, что у вас есть основной проект и отдельный репозиторий с общими библиотеками. Вместо копирования файлов вы подключаете этот репозиторий как подмодуль. Так вы можете обновлять библиотеку независимо и при этом контролировать, какая именно версия используется в основном проекте.
Работа с подмодулями строится вокруг трёх базовых команд:
- git submodule add — добавление нового подмодуля
- git submodule init — инициализация подмодулей для текущего клона
- git submodule update — загрузка содержимого подмодулей и переключение на нужные коммиты
В этой статье мы подробно разберём именно инициализацию подмодулей — что делает команда git submodule init, когда она нужна, чем отличается от git submodule update и как корректно выстраивать рабочий процесс с подмодулями, чтобы не сталкиваться с неожиданными ошибками.
Давайте разберёмся по шагам и на реальных примерах.
Что такое подмодуль в Git
Общая идея подмодулей
Подмодуль — это ссылку на другой Git-репозиторий, который "встраивается" в ваш основной репозиторий как подкаталог, но живёт своей жизнью:
- у него свой собственный репозиторий и история коммитов
- он может использоваться несколькими проектами
- в основном репозитории хранится только информация о том, на какой коммит внутри подмодуля он ссылается
Смотрите, как это выглядит на практике:
- Основной репозиторий: /my-app
- Подмодуль: /my-app/libs/common (внутри лежит другой Git-репозиторий)
Главная особенность: в основном репозитории хранится не всё содержимое подмодуля, а только "указатель" на конкретный коммит подмодуля. При клонировании родительского репозитория вам нужно отдельно скачать содержимое подмодулей — именно здесь и появляется команда git submodule init.
Файлы, связанные с подмодулями
Когда вы используете подмодули, в корне репозитория появляется специальный служебный файл .gitmodules. В нём Git хранит конфигурацию подмодулей: пути, URL, имена.
Пример содержимого .gitmodules:
[submodule "libs/common"]
path = libs/common # Путь к подмодулю внутри основного репо
url = https://github.com/example/common-lib.git # URL удалённого репо
Кроме файла .gitmodules, Git также хранит настройки подмодулей в конфигурации репозитория (.git/config), и именно сюда попадает информация при выполнении git submodule init. Сейчас давайте посмотрим, что это за шаг и зачем он нужен.
Что делает команда git submodule init
Основное назначение git submodule init
Команда git submodule init выполняет инициализацию подмодулей для конкретного клона репозитория. Проще говоря, она:
- читает файл .gitmodules
- создаёт локальные записи конфигурации подмодулей в .git/config
- подготавливает репозиторий к дальнейшему выполнению git submodule update
Важно: git submodule init НЕ скачивает содержимое подмодулей и НЕ переключает их на нужные коммиты. Она только настраивает конфигурацию.
Давайте посмотрим на базовый пример:
git clone https://github.com/example/my-app.git
cd my-app
# Инициализируем подмодули
git submodule init
# Загружаем содержимое подмодулей
git submodule update
Комментарий к шагам:
- после git clone вы видите директории подмодулей, но они пустые или содержат только метаданные
- команда git submodule init читает .gitmodules и регистрирует подмодули в локальной конфигурации
- команда git submodule update уже использует эту конфигурацию, чтобы скачать данные и перейти на фиксированные коммиты
Где хранится информация после init
Когда вы выполняете git submodule init, Git переносит настройки из .gitmodules в локальный .git/config. Смотрите, как это выглядит.
Содержимое .gitmodules:
[submodule "libs/common"]
path = libs/common
url = https://github.com/example/common-lib.git
После git submodule init в .git/config появится примерно такое:
[submodule "libs/common"]
url = https://github.com/example/common-lib.git # URL подмодуля
Здесь я показываю только ключевой параметр url. При необходимости сюда могут добавляться и другие опции, если вы их настроите.
Зачем это нужно:
- .gitmodules хранится в репозитории и версионируется
- .git/config — локальная конфигурация, её можно изменять под конкретную машину (например, заменить HTTPS на SSH), не затрагивая общий репозиторий
git submodule init как раз делает этот перенос и создаёт базу для переопределений.
Почему init не всегда выполняется автоматически
Вы можете заметить, что во многих руководствах вместо связки команд
git submodule init
git submodule update
используют одну:
git submodule update --init
Этот вариант действительно выполняет и инициализацию, и загрузку содержимого. Но команда git submodule init остаётся полезной в случаях, когда вам нужно:
- просто зарегистрировать подмодули, но не загружать их содержимое сейчас
- изменить конфигурацию (например, URL подмодуля), прежде чем загружать его
- инициализировать только часть подмодулей
Поэтому команда init — это отдельный осознанный шаг, который даёт больше гибкости.
Базовые сценарии использования git submodule init
Сценарий 1: Клонирование репозитория с подмодулями
Частый случай — вы клонируете проект, в котором уже настроены подмодули. Давайте разберём типичный рабочий процесс.
Шаг 1. Клонируем основной репозиторий:
git clone https://github.com/example/my-app.git
cd my-app
Шаг 2. Инициализируем подмодули:
git submodule init
Комментарий:
- Git читает файл .gitmodules
- для каждого подмодуля добавляет секцию в .git/config
- при необходимости вы можете скорректировать URL в .git/config (например, заменить HTTPS на SSH)
Шаг 3. Обновляем и загружаем подмодули:
git submodule update
Комментарий:
- Git клонирует каждый подмодуль в указанную директорию
- переключает его на коммит, зафиксированный в основном репозитории
- в подкаталоге подмодуля появляется полноценный Git-репозиторий со своей историей
Если вы хотите всё сделать одной командой, можно использовать:
git submodule update --init --recursive
Но мы сосредотачиваемся на явной инициализации, чтобы вы лучше понимали, что происходит на каждом шаге.
Сценарий 2: Инициализация только одного подмодуля
В большом проекте с десятками подмодулей вы можете не хотеть инициализировать и загружать все сразу. Смотрите, как можно работать выборочно.
Пусть в .gitmodules у вас есть два подмодуля:
[submodule "libs/common"]
path = libs/common
url = https://github.com/example/common-lib.git
[submodule "libs/ui"]
path = libs/ui
url = https://github.com/example/ui-lib.git
Если вам пока нужна только библиотека common, вы можете инициализировать её по имени:
git submodule init libs/common
git submodule update libs/common
Важно: в git submodule init можно передавать не только путь, но и имя подмодуля. Обычно имя совпадает с тем, что в кавычках в .gitmodules ("libs/common"), а путь — это значение поля path. Как правило, проще указывать путь.
Пример с комментарием:
# Инициализируем конфигурацию только для подмодуля в каталоге libs/common
git submodule init libs/common
# Загружаем содержимое только этого подмодуля
git submodule update libs/common
Такой подход экономит время и трафик, если часть подмодулей вам не нужна.
Сценарий 3: Перенастройка URL перед загрузкой подмодулей
Иногда в .gitmodules прописан HTTPS-URL, а вы хотите использовать SSH (или наоборот). В этом случае удобно:
- выполнить git submodule init
- поменять URL в .git/config
- только потом вызвать git submodule update
Давайте разберём пример.
Предположим, в .gitmodules указано:
[submodule "libs/common"]
path = libs/common
url = https://github.com/example/common-lib.git
Вы выполняете:
git submodule init
Теперь в .git/config появляется запись:
[submodule "libs/common"]
url = https://github.com/example/common-lib.git
Вы можете вручную изменить её, например так:
[submodule "libs/common"]
url = git@github.com:example/common-lib.git
И только после этого выполнить:
git submodule update libs/common
Обратите внимание: изменение .git/config не затрагивает файл .gitmodules, то есть не влияет на других разработчиков, работающих с проектом. Это локальная настройка.
Отличие git submodule init от git submodule update
Логическая разница команд
Давайте ещё раз чётко разделим роли:
- git submodule init — настраивает конфигурацию подмодулей в текущем клоне на основе .gitmodules
- git submodule update — физически скачивает содержимое подмодулей и переключает их на нужные коммиты
Вы можете воспринимать это как два этапа:
- Настройка "что и откуда" (init)
- Загрузка "чего именно" (update)
Здесь я привожу небольшой пример, который покажет разницу на практике.
# После клонирования репозитория с подмодулями
git submodule init # Настраиваем конфигурацию
git submodule update # Клонируем и переключаем подмодули
Если вы попробуете выполнить только git submodule update без init, но при этом в .git/config ещё нет конфигурации подмодулей, Git просто не будет знать, откуда брать URL. В реальных проектах это чаще всего решается одной командой:
git submodule update --init
Но понимание разницы помогает в ситуациях с ручной настройкой, изменением URL и выборочной работой с подмодулями.
Зачем иногда удобно разделять init и update
Разделение на два шага полезно в таких случаях:
- когда есть разные окружения (например, разработка, CI, staging, production) и вы хотите на каждом окружении переопределять URL подмодулей
- когда часть подмодулей требует доступа по SSH, который не настроен на CI-сервере
- когда размер подмодулей большой, и вы хотите загружать их содержимое "по требованию"
Смотрите, как может выглядеть скрипт развёртывания, использующий разделение:
# Инициализируем конфигурацию подмодулей
git submodule init
# Здесь мы можем программно изменить URL в .git/config,
# например, подставить зеркала или внутренние репозитории компании
# После всех корректировок загружаем подмодули
git submodule update --recursive
Комментарии в скрипте подсказывают, где удобно встроить дополнительные шаги по настройке.
Практическая работа с .gitmodules и git submodule init
Структура файла .gitmodules
Файл .gitmodules хранится в корне основного репозитория и участвует в версионировании. В нём описаны все подмодули, которые используются проектом.
Пример файла с двумя подмодулями:
[submodule "libs/common"]
path = libs/common # Путь к каталогу подмодуля
url = https://github.com/example/common-lib.git # URL удалённого репо
[submodule "libs/ui"]
path = libs/ui
url = https://github.com/example/ui-lib.git
Параметры:
- submodule "имя" — логическое имя подмодуля, которое Git использует во внутренней конфигурации
- path — относительный путь к каталогу, где будет располагаться подмодуль
- url — адрес удалённого репозитория подмодуля
Когда вы вызываете git submodule init, Git использует этот файл, чтобы сформировать соответствующие секции в .git/config.
Как проверить, что инициализация прошла успешно
После выполнения git submodule init вы можете:
- Посмотреть конфигурацию подмодулей:
git config --file .gitmodules --name-only --get-regexp path
# Выводит пути подмодулей из .gitmodules
git config --file .git/config --name-only --get-regexp submodule
# Выводит секции подмодулей из локальной конфигурации
- Проверить статус подмодулей:
git submodule status
Типичный вывод:
-3a1b2c4 libs/common # Подмодуль не инициализирован или не загружен
-7f9d8e1 libs/ui
Символ "-" в начале говорит о том, что подмодуль не инициализирован или не обновлён. После выполнения init + update вы увидите примерно такой формат:
3a1b2c4 libs/common (heads/main)
7f9d8e1 libs/ui (heads/main)
Теперь знак "-" исчез, а перед хешем стоит пробел — это означает, что подмодуль инициализирован и находится на нужном коммите.
Что происходит, если .gitmodules изменился
Представьте, что коллега добавил новый подмодуль и закоммитил обновлённый .gitmodules. Вы подтягиваете изменения:
git pull
В этот момент ваш локальный .git/config ещё не знает о новом подмодуле. Чтобы зарегистрировать его, вы снова выполняете:
git submodule init
Git посмотрит в .gitmodules, найдёт новые записи и добавит их в .git/config. Уже существующие подмодули повторно не пострадают — это операция "добавления того, чего ещё нет".
После этого вы можете загрузить содержимое нового подмодуля:
git submodule update
Если в проекте несколько новых подмодулей, команда init обработает их все.
Расширенные приёмы использования git submodule init
Инициализация подмодулей в глубину (рекурсивно)
Во многих проектах подмодуль сам может содержать свои подмодули. Например:
- основной репозиторий my-app использует подмодуль libs/common
- подмодуль libs/common сам использует подмодуль vendor/logger
В таком случае вы можете инициализировать и обновить всю цепочку подмодулей рекурсивно:
git submodule update --init --recursive
Но иногда вам нужно именно разделить шаги. Тогда последовательность может быть такой:
# Инициализируем подмодули верхнего уровня
git submodule init
# Загружаем их содержимое вместе с их подмодулями
git submodule update --recursive
Или, если вы хотите вручную контролировать рекурсивную инициализацию, можно:
# Инициализируем все подмодули верхнего уровня
git submodule init
# Для каждого подмодуля переходим внутрь и выполняем те же команды
cd libs/common
# Инициализируем подмодули внутри подмодуля
git submodule init
git submodule update
Комментарии:
- такой ручной подход нужен редко, но помогает в нестандартных конфигурациях
- чаще всего достаточно использовать флаг --recursive сразу в update
Частичная инициализация подмодулей для оптимизации
Если у вас монорепозиторий с большим количеством подмодулей, вы можете ускорить работу, инициализируя только нужные части.
Представим, что в .gitmodules у вас 10 подмодулей, но для вашей задачи нужны только 2. Тогда вы можете сделать так:
# Инициализируем только нужные подмодули по путям
git submodule init libs/common libs/ui
# Загружаем только их содержимое
git submodule update libs/common libs/ui
Обратите внимание:
- git submodule init принимает список путей подмодулей
- если вы случайно укажете путь, которого нет в .gitmodules, Git выдаст ошибку
Такой способ особенно полезен на CI, где вы хотите экономить время сборки.
Использование подмодулей при работе с форками
Частая ситуация: вы форкнули основной репозиторий, но подмодули в нём всё ещё ссылаются на оригинальные репозитории. Иногда это нормально, иногда вы хотите, чтобы подмодули тоже указывали на ваши форки.
Здесь практическая стратегия может выглядеть так:
- Клонируете форк основного репозитория
- Выполняете git submodule init
- Меняете URL подмодулей в .git/config (на свои форки)
- Выполняете git submodule update
Пример команд:
git clone git@github.com:my-org/my-app-fork.git
cd my-app-fork
# Инициализируем подмодули
git submodule init
# Меняем URL подмодуля libs/common на форк
git config submodule.libs/common.url git@github.com:my-org/common-lib-fork.git
# Загружаем подмодуль уже с новым URL
git submodule update libs/common
Комментарий:
- здесь мы не трогаем .gitmodules, чтобы не ломать исходную структуру проекта
- все изменения остаются локальными в .git/config
- такой подход удобен, если вы только тестируете свои модификации подмодулей
Типичные проблемы при инициализации подмодулей и как их решать
Проблема: подмодуль пустой после init
Ситуация: вы сделали git submodule init, заглянули в директорию подмодуля и увидели пустой каталог или только структуру без файлов.
Причина: init не скачивает содержимое подмодуля, он только регистрирует конфигурацию.
Решение:
# Инициализируем конфигурацию
git submodule init
# Загружаем содержимое и переключаем на нужный коммит
git submodule update
Если есть вложенные подмодули:
git submodule update --recursive
Проблема: неправильный или устаревший URL подмодуля
Ситуация: репозиторий подмодуля переехал, и URL в .gitmodules устарел. При выполнении git submodule update вы получаете ошибку клонирования.
Общая стратегия:
- Исправить URL в .gitmodules
- Повторно выполнить git submodule init (для синхронизации c .git/config)
- Обновить подмодуль
Пример:
# Открываем .gitmodules и меняем url:
# url = https://new-host.com/example/common-lib.git
# Синхронизируем конфиг подмодулей
git submodule init
# Либо более надёжно - используем sync
git submodule sync
# После этого снова пробуем обновить
git submodule update
Команда git submodule sync помогает синхронизировать URL между .gitmodules и .git/config. Если вы уже меняли URL вручную в .git/config, этот шаг особенно полезен.
Проблема: забыли выполнить init на новом клоне
Ситуация: вы клонировали репозиторий, перешли в каталог подмодуля, но видите странное состояние — нет истории, HEAD в detached-состоянии, файлы отсутствуют или ведут себя неожиданно.
Решение:
- Убедитесь, что подмодуль инициализирован и обновлён:
git submodule init
git submodule update
- Проверьте статус:
git submodule status
Если всё корректно, вы увидите хеши коммитов без дефиса в начале строки.
Проблема: подмодули не инициализируются автоматически на CI
На CI нередко забывают явно вызывать команды для подмодулей. В результате сборка падает, потому что часть кода отсутствует.
Минимальный набор действий на CI:
git submodule init
git submodule update --recursive
Либо одной командой:
git submodule update --init --recursive
Комментарий:
- если вам нужна гибкость (например, переопределение URL на CI), лучше разделять команды
- если достаточно стандарта, можно использовать update --init
Заключение
Команда git submodule init — это важный шаг в рабочем процессе с подмодулями Git. Она не скачивает код, а настраивает конфигурацию подмодулей на основе файла .gitmodules, создавая записи в локальном .git/config. Именно после этого Git "понимает", какие подмодули есть в проекте и откуда их нужно загружать.
Ключевые моменты, которые стоит удержать:
- init подготавливает локальный клон к работе с подмодулями, но не загружает их содержимое
- update — отвечает за клонирование подмодулей и переключение их на нужные коммиты
- связка init + настройка URL + update даёт гибкость при работе с разными окружениями, форками и зеркалами
- частичная инициализация по путям помогает ускорить работу в больших репозиториях
- повторный init безопасен и полезен после изменений в .gitmodules
Используя git submodule init осознанно, вы уменьшаете количество "магии" в поведении подмодулей и лучше контролируете, что происходит в вашем проекте при клонировании и обновлении зависимостей.
Частозадаваемые технические вопросы по теме инициализации подмодулей
Вопрос 1. Как переинициализировать подмодуль, если я его удалил вручную из файловой системы
Если вы случайно удалили каталог подмодуля, но записи в Git остались, можно восстановиться так:
# Убедитесь, что конфигурация подмодуля есть
git submodule init
# Восстанавливаем содержимое каталога подмодуля
git submodule update --libs/common
Комментарии:
- git submodule init повторно не ломает конфигурацию, он просто убеждается, что записи есть
- update заново клонирует и заполняет каталог подмодуля
Вопрос 2. Как перенести подмодуль в другой каталог и правильно переинициализировать
Шаги:
- Обновите путь в .gitmodules:
[submodule "libs/common"]
path = libs/common-v2 # Новый путь
url = https://github.com/example/common-lib.git
- Выполните:
git mv libs/common libs/common-v2 # Перемещаем каталог
git add .gitmodules libs/common-v2
git commit -m "Перемещен подмодуль libs/common в libs/common-v2"
- На новом клоне после pull выполните:
git submodule init
git submodule update
Git подхватит новый путь из .gitmodules и корректно инициализирует подмодуль.
Вопрос 3. Можно ли полностью отключить подмодуль, чтобы он не инициализировался на моей машине
Да, вы можете удалить запись о подмодуле из локального .git/config:
git config --unset submodule.libs/common.url
После этого git submodule init снова добавит эту запись из .gitmodules. Если вы хотите, чтобы подмодуль вообще не участвовал в вашей работе, просто не вызывайте init и удалите каталог:
rm -rf libs/common
Но помните, что при этом сборка проекта может не пройти, если этот код нужен.
Вопрос 4. Как инициализировать подмодуль в режиме только для чтения с использованием другого протокола
Можно использовать init, затем переопределить URL только для чтения в .git/config:
git submodule init
# Меняем URL на протокол только для чтения, например git
git config submodule.libs/common.url git://github.com/example/common-lib.git
git submodule update libs/common
В итоге вы не трогаете .gitmodules, но локально используете нужный протокол.
Вопрос 5. Как увидеть, какие подмодули ещё не были инициализированы
Можно сравнить список путей в .gitmodules и наличие конфигураций в .git/config:
# Список путей подмодулей из .gitmodules
git config --file .gitmodules --get-regexp path
# Список подмодулей, известных локальной конфигурации
git config --get-regexp submodule.*.url
Если подмодуль есть в .gitmodules, но нет в выводе второй команды, значит вы ещё не выполняли git submodule init для этого подмодуля.
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

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