Тур по Git
Продвинутый Git / Урок 5.11

Подмодули и поддеревья

Иногда проекту нужно включить в себя другой Git-репозиторий — общую библиотеку, набор конфигураций или вендорную зависимость. Git предлагает два подхода для этого: подмодули и поддеревья.

Git-подмодули

Подмодуль — это ссылка на конкретный коммит в другом репозитории. Родительский репозиторий не хранит код подмодуля напрямую — он хранит указатель.

Добавление подмодуля

git submodule add https://github.com/example/library.git libs/library

Это создаёт:

  • Директорию libs/library/ со склонированным репозиторием
  • Файл .gitmodules, записывающий URL и путь
  • Запись в индексе, указывающую на конкретный коммит

Клонирование репозитория с подмодулями

Когда кто-то клонирует ваш репозиторий, директории подмодулей будут пустыми. Им нужно выполнить:

git submodule update --init --recursive

Или клонировать с подмодулями за один шаг:

git clone --recurse-submodules https://github.com/example/project.git

Обновление подмодуля

cd libs/library
git pull origin main
cd ../..
git add libs/library
git commit -m "Update library submodule"

Типичные ошибки

  • Забыли инициализировать — в свежесклонированных репозиториях директории подмодулей пустые
  • Отсоединённый HEAD — подмодули извлекают конкретный коммит, а не ветку; вы всегда находитесь в состоянии отсоединённого HEAD внутри подмодуля
  • Рассинхронизированные указатели — если вы обновите подмодуль, но забудете закоммитить родительский репозиторий, коллеги не увидят обновление
  • CI/CD — системы сборки должны быть настроены на инициализацию подмодулей

Git Subtree

Поддерево объединяет код другого репозитория непосредственно в ваш репозиторий. Нет указателя — файлы фактически являются частью вашего проекта.

Добавление поддерева

git subtree add --prefix=libs/library https://github.com/example/library.git main --squash

Флаг --squash сжимает историю библиотеки в один коммит.

Обновление поддерева

git subtree pull --prefix=libs/library https://github.com/example/library.git main --squash

Отправка изменений обратно

Если вы изменили код поддерева и хотите отправить его в исходный репозиторий:

git subtree push --prefix=libs/library https://github.com/example/library.git main

Подмодуль и поддерево

АспектПодмодульПоддерево
ХранениеУказатель на внешний коммитФайлы объединены в репозиторий
КлонированиеТребуется --recurse-submodulesРаботает из коробки
Настройка для коллегНужно выполнить submodule update --initНичего дополнительного
ИсторияОтдельная история репозиторияОбъединена с историей проекта
Обновление из источникаPull внутри подмодуля, коммит родителяgit subtree pull
СложностьВыше — дополнительные команды, легко ошибитьсяНиже — стандартные операции Git

Когда использовать каждый подход

Используйте подмодули, когда:

  • Зависимость большая и вы не хотите включать её в свою историю
  • Вам нужно зафиксировать точную версию зависимости
  • Подмодуль активно разрабатывается и имеет собственный цикл релизов

Используйте поддеревья, когда:

  • Простота важнее разделения
  • Коллеги не должны учить дополнительные команды
  • Вы хотите самодостаточный репозиторий, работающий без доступа к интернету

Ключевые моменты

  • Подмодули хранят указатель на внешний репозиторий; поддеревья объединяют код напрямую
  • Подмодули требуют дополнительных шагов настройки (--init, --recurse-submodules)
  • Поддеревья проще для коллег, но смешивают истории
  • Оба подхода решают задачу встраивания одного репозитория внутрь другого