Обновление подмодулей в Git - git submodule update

17 декабря 2025
Автор

Олег Марков

Введение

Подмодули в Git позволяют встроить один репозиторий внутрь другого как зависимость. Это удобно, когда вы хотите подключить стороннюю библиотеку как исходный код или разделить большой проект на несколько частей.

Но вместе с этим появляется вопрос - как правильно обновлять подмодули, чтобы:

  • не потерять локальные изменения;
  • не сломать сборку;
  • не запутаться в версиях зависимостей;
  • избежать конфликтов между основным репозиторием и подмодулями.

Здесь и вступает в игру команда git submodule update. Именно она отвечает за то, чтобы подмодули соответствовали тем версиям, которые зафиксированы в основном репозитории.

В этой статье вы увидите, как работает git submodule update, разберете ключевые флаги и типичные сценарии использования. Я буду опираться на практические примеры, чтобы вы могли сразу применить их в реальной работе.


Что такое подмодуль и что именно обновляет git submodule update

Подмодуль Git — это ссылка на другой Git‑репозиторий, зафиксированная в конкретном коммите. Важно понимать две вещи:

  1. В основном репозитории хранится не сама история подмодуля, а только:

    • URL подмодуля;
    • путь к нему в рабочей директории;
    • конкретный коммит, на который указывает подмодуль.
  2. Подмодуль — это по сути отдельный репозиторий внутри папки проекта:

    • у него есть свой .git (или ссылка на него);
    • своя история;
    • свои ветки.

Файл .gitmodules в корне проекта описывает подмодули. Пример конфигурации:

[submodule "libs/mylib"]
    path = libs/mylib         # Путь к подмодулю в проекте
    url = git@github.com:org/mylib.git   # Репозиторий подмодуля
    branch = main             # Необязательный параметр - используемая ветка

А в индексе Git основной репозиторий хранит только хеш коммита подмодуля. Смотрите, как это выглядит:

# Покажем статус подмодулей
git submodule status
#  3f2a9c7d8e1c0b4d5f6a7b8c9d0e1f2a3b4c5d6 libs/mylib
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ хеш коммита
# путь к подмодулю -> libs/mylib

Теперь главный момент:

  • git submodule update не "обновляет" подмодуль до последнего коммита в его ветке;
  • он синхронизирует рабочую копию подмодуля с тем коммитом, который уже записан в основном репозитории.

То есть команда подтягивает состояние подмодулей под то, что уже зафиксировано в команде разработчиков (в коммитах основного репозитория), а не "самостоятельно обновляет зависимости до новых версий".


Базовый синтаксис git submodule update

Давайте посмотрим на базовый синтаксис:

git submodule update [<путь_к_подмодулю> ...]

Без дополнительных флагов команда делает следующее:

  1. Считывает информацию о подмодулях из:

    • файла .gitmodules;
    • индекса и HEAD основного репозитория.
  2. Для каждого подмодуля:

    • извлекает (fetch) нужные объекты из удаленного репозитория (если их нет локально);
    • переключает рабочую копию подмодуля на тот коммит, который записан в основном репозитории;
    • оставляет подмодуль в состоянии detached HEAD (голова отсоединена).

Простой пример:

# Обновить все уже инициализированные подмодули до нужных коммитов
git submodule update

Если вы хотите обновить только один конкретный подмодуль, укажите путь:

# Обновить только подмодуль в папке libs/mylib
git submodule update libs/mylib

Обратите внимание: если вы впервые клонировали репозиторий с подмодулями, простой git clone не скачает содержимое подмодулей. После клонирования вы увидите пустые папки или непроинициализированные директории. В этом случае используют связку:

git submodule init    # Инициализация конфигурации подмодулей локально
git submodule update  # Загрузка и установка нужных коммитов

Ниже мы разберем, как сократить это до одной команды и какие существуют расширенные режимы.


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

Быстрый способ - clone с параметром --recurse-submodules

Вместо последовательности из init и update чаще используют вариант:

git clone --recurse-submodules git@github.com:org/main-repo.git
# Параметр --recurse-submodules:
# - автоматически инициализирует подмодули
# - выполняет git submodule update для них

Смотрите, что происходит:

  1. Клонируется основной репозиторий.
  2. Читается файл .gitmodules.
  3. Для каждого подмодуля выполняется:
    • настройка URL;
    • загрузка содержимого;
    • переключение на зафиксированный коммит.

Этот способ особенно удобен, когда вы подключаете новый проект и хотите сразу иметь рабочее состояние.

Когда нужен отдельный git submodule init

Команда git submodule init настраивает локальную конфигурацию подмодулей, но не загружает их содержимое. Это полезно, когда:

  • файл .gitmodules был изменен, добавили новый подмодуль;
  • вы уже работали с репозиторием, но кто-то из коллег добавил еще подмодули.

Пример:

# Коллега добавил подмодуль в проект
git pull origin main

# Теперь инициализируем только новые подмодули
git submodule init

# И затем подтягиваем их содержимое
git submodule update

Комбинация init + update по сути эквивалентна тому, что делает --recurse-submodules при клонировании, только применяется позже, уже в существующем рабочем репозитории.


Ключевые флаги git submodule update и когда их использовать

Теперь давайте перейдем к более интересным режимам работы git submodule update. Они задают поведение команды в разных сценариях.

--init — инициализация и обновление в одной команде

Флаг --init позволяет объединить инициализацию и обновление:

git submodule update --init
# Сначала инициализация подмодулей (как git submodule init)
# Затем загрузка и установка нужных коммитов

Вы можете указать конкретный подмодуль:

git submodule update --init libs/mylib

Такой вариант удобен, когда вы не уверены, инициализирован ли подмодуль у текущего разработчика, а хотите дать одну универсальную команду в инструкции.

Например, в README можно написать:

# Подготовка репозитория к сборке
git submodule update --init --recursive

Здесь сразу задействован еще один флаг — о нем дальше.

--recursive — рекурсивное обновление вложенных подмодулей

Подмодули сами могут содержать подмодули. Тогда простой git submodule update обновит только первый уровень, а вложенные останутся нетронутыми.

Флаг --recursive решает эту проблему:

git submodule update --init --recursive
# Инициализирует и обновляет:
# - подмодули первого уровня
# - подмодули внутри них
# - и так далее на всю глубину

Обратите внимание:

  • Если вы работаете с проектами, где есть несколько уровней подмодулей (например, монорепозитории или наборы библиотек), используйте этот флаг почти всегда.
  • Для CI-сборок это часто стандартная команда.

--remote — обновление подмодулей по ветке, а не по зафиксированному коммиту

Обычно git submodule update приводит подмодуль к конкретному коммиту, записанному в основном репозитории. Но иногда вы хотите обновлять подмодуль до последнего коммита в его ветке (например, в ветке main).

Для этого есть режим --remote:

git submodule update --remote

Что делает эта команда:

  1. Для каждого подмодуля:
    • читает в .gitmodules, какая ветка указана в параметре branch;
    • делает fetch изменений из origin;
    • переключает подмодуль на последний коммит этой ветки;
    • оставляет его в этой ветке (HEAD не будет detached).

Пример конфигурации:

[submodule "libs/mylib"]
    path = libs/mylib
    url = git@github.com:org/mylib.git
    branch = main     # Именно эта ветка используется с --remote

Пример использования:

# Обновить все подмодули до актуальных коммитов их веток
git submodule update --remote

# Обновить только конкретный подмодуль по ветке
git submodule update --remote libs/mylib

Обратите внимание на важный момент: после такого обновления вам нужно зафиксировать в основном репозитории новый коммит подмодуля:

cd libs/mylib
git log -1  # Смотрите, какой новый коммит

cd ..
git add libs/mylib
git commit -m "Обновлен подмодуль libs/mylib до актуальной версии"

Иначе ваши коллеги, выполнив обычный git submodule update (без --remote), не получат новый коммит, так как основной репозиторий все еще указывает на старое состояние.

--merge и --rebase — когда есть локальные изменения в подмодуле

Нередкая ситуация: вы вносите локальные изменения в подмодуль, а затем хотите обновить его под нужный коммит, не потеряв свою работу.

По умолчанию git submodule update:

  • просто принудительно переключит подмодуль на нужный коммит;
  • если в подмодуле есть незакоммиченные изменения, команда завершится с ошибкой.

Чтобы аккуратно совмещать локальные изменения с обновлениями, используются режимы --merge и --rebase.

--merge

Режим --merge делает следующее:

  1. Обновляет подмодуль до нужного коммита.
  2. Поверх этого делает merge с вашей текущей веткой.

Пример:

git submodule update --remote --merge libs/mylib
# 1. Обновит подмодуль до нового коммита ветки branch из .gitmodules
# 2. Попробует слить изменения с вашей текущей веткой в подмодуле

Как это выглядит в шаги:

cd libs/mylib
# Подмодуль переключен на новый коммит
# Git выполняет что-то вроде:
# git merge <старый_коммит/ваша_ветка>

Если будут конфликты, Git попросит их решить, вы сделаете обычный merge и commit.

--rebase

Режим --rebase похож, но история становится линейной:

git submodule update --remote --rebase libs/mylib

Здесь Git:

  1. Обновит подмодуль до нового коммита ветки.
  2. "Перенесет" ваши локальные коммиты поверх новых (rebase).

Это удобно, если вы хотите линейную историю без merge-коммитов внутри подмодулей.

Выбирайте:

  • --merge — если вас устраивают merge-коммиты;
  • --rebase — если вы любите чистую линейную историю.

Типичные сценарии использования git submodule update

Теперь давайте разберем практические сценарии, с которыми вы будете сталкиваться чаще всего.

Сценарий 1. Вы клонировали репозиторий с подмодулями

Рекомендуемая последовательность:

git clone --recurse-submodules git@github.com:org/main-repo.git
# Репозиторий и все подмодули готовы к работе

Если вы уже клонировали без параметра:

git clone git@github.com:org/main-repo.git
cd main-repo

# Инициализация и обновление всех подмодулей
git submodule update --init --recursive

Здесь я использую --recursive на случай наличия вложенных подмодулей.

Сценарий 2. Коллега обновил версию подмодуля, вы делаете git pull

Часто в команде кто-то обновляет подмодуль (фиксирует новый коммит в основном репозитории). Вы делаете:

git pull

После этого у вас в рабочей копии подмодуль может находиться "не на том" коммите. Статус будет выглядеть примерно так:

git status
# modified:   libs/mylib (new commits)

Это не ваши изменения в подмодуле, а просто расхождение между:

  • коммитом подмодуля в рабочей папке;
  • коммитом, на который ссылается основной репозиторий.

Решение:

git submodule update --recursive

Теперь подмодуль будет приведен к коммиту, зафиксированному в полученном вами коммите основного репозитория.

Сценарий 3. Вы хотите обновить подмодуль до свежего состояния его ветки

Предположим:

  • подмодуль указывает на ветку main;
  • вы хотите подтянуть последние изменения из этой ветки.

Настройка в .gitmodules:

[submodule "libs/mylib"]
    path = libs/mylib
    url = git@github.com:org/mylib.git
    branch = main

Далее вы выполняете:

# Обновить подмодуль до последнего коммита ветки main
git submodule update --remote libs/mylib

Теперь вы увидите новый коммит в подмодуле:

cd libs/mylib
git log -1  # Здесь уже последний коммит из origin/main

После этого важно:

cd ..
git add libs/mylib
git commit -m "Обновлен подмодуль libs/mylib до последней версии main"
git push

Теперь коллеги смогут просто выполнить:

git pull
git submodule update

и получить ваш обновленный подмодуль.

Сценарий 4. Подмодуль с локальными изменениями, а вам нужно обновить его версию

Представьте, что вы правили код в подмодуле, а в самом подмодуле появились новые коммиты от коллег.

Шаги:

  1. Сначала убедимся, что ваши изменения в подмодуле закоммичены:
cd libs/mylib
git status
# Если есть изменения - делаем коммит
git add .
git commit -m "Локальные правки в подмодуле"
  1. Теперь из основного репозитория:
cd ..
git submodule update --remote --rebase libs/mylib
# или --merge, если предпочитаете merge-историю
  1. Разрешаем возможные конфликты в подмодуле:

    • заходим в папку подмодуля;
    • решаем конфликты;
    • делаем git rebase --continue или git commit в зависимости от режима.
  2. Фиксируем обновленный коммит подмодуля в основном репозитории:

cd ..
git add libs/mylib
git commit -m "Обновлен подмодуль libs/mylib с учетом локальных изменений"

Важные нюансы работы git submodule update

Detached HEAD в подмодулях — почему это нормально

После обычного git submodule update вы окажетесь в подмодуле в состоянии detached HEAD:

cd libs/mylib
git status
# HEAD detached at 3f2a9c7

Это значит:

  • вы находитесь на конкретном коммите, не привязанном к локальной ветке;
  • любые новые коммиты будут "висячими", если не создать ветку.

Для чтения кода и сборки это не проблема. Но если вы хотите вносить изменения в подмодуль, лучше переключиться на ветку:

cd libs/mylib
git checkout main          # или другая нужная ветка
# Теперь можно коммитить как обычно

Если вы используете git submodule update --remote и в .gitmodules указана branch, то Git сразу будет переключать подмодуль на эту ветку, а не оставлять в detached HEAD.

Разница между git submodule update и git pull внутри подмодуля

Может возникнуть соблазн заходить в папку подмодуля и делать там git pull. Смотрите, чем это чревато:

  • git pull действительно подтянет новые коммиты подмодуля;
  • но основной репозиторий не узнает о том, что подмодуль теперь указывает на другой коммит, пока вы не сделаете git add путькподмодулю и git commit.

Если вы просто сделаете git pull в подмодуле и ничего не зафиксируете в основном проекте, коллеги не смогут воспроизвести ваше состояние одним git submodule update.

Правильный подход:

  1. Если обновляете подмодуль как зависимость для проекта:

    • делайте git submodule update --remote;
    • фиксируйте новый коммит подмодуля в основном репозитории.
  2. Если вы именно разрабатываете сам подмодуль:

    • можете работать в нем как в обычном репозитории;
    • но при смене "версии зависимости" для основного проекта все равно нужно обновить ссылку в основном репозитории.

Работа с несколькими удаленными репозиториями подмодуля

Иногда подмодуль имеет несколько remotes. git submodule update по умолчанию ориентируется на origin. Если вы хотите использовать другой remote, вам нужно:

  1. Зайти в подмодуль и настроить нужный remote.
  2. При необходимости изменить url в .gitmodules, чтобы остальные разработчики подтягивали то же самое.

Пример:

cd libs/mylib
git remote add upstream git@github.com:other-org/mylib.git
git fetch upstream
git checkout -b feature/upstream upstream/main

# После этого вы можете обновить подмодуль до нужного коммита
cd ..
git add libs/mylib
git commit -m "Переключен подмодуль libs/mylib на новую ветку"

git submodule update будет работать с тем коммитом, который вы зафиксируете в основном репозитории, независимо от того, с какого remote этот коммит был получен.


Практическая инструкция по работе с подмодулями и их обновлением

Здесь я соберу типичные команды в одном месте, чтобы вам было проще использовать их как шпаргалку.

Первичная настройка проекта с подмодулями

# Клонируем проект со всеми подмодулями
git clone --recurse-submodules git@github.com:org/main-repo.git

cd main-repo

# На всякий случай обновляем подмодули рекурсивно
git submodule update --init --recursive

Обновление подмодулей после git pull

# Получаем последние изменения основного репозитория
git pull

# Приводим подмодули к нужным коммитам
git submodule update --recursive

Обновление подмодуля до последнего коммита его ветки

# Убедитесь, что в .gitmodules указана ветка для подмодуля
# branch = main

# Обновляем подмодуль до конца ветки
git submodule update --remote libs/mylib

# Фиксируем новую версию подмодуля в основном репозитории
git add libs/mylib
git commit -m "Обновлен подмодуль libs/mylib до последних изменений main"
git push

Внесение правок в подмодуль и обновление его версии в проекте

# Переходим в подмодуль
cd libs/mylib

# Переключаемся на ветку разработки
git checkout main

# Вносим правки, коммитим
# ... ваши изменения ...
git add .
git commit -m "Исправление бага в библиотеке"

# Отправляем изменения в удаленный репозиторий подмодуля
git push origin main

# Возвращаемся в основной проект
cd ..

# Фиксируем новый коммит подмодуля
git add libs/mylib
git commit -m "Подмодуль libs/mylib обновлен до нового коммита main"
git push

Обновление подмодуля при наличии локальных коммитов

# Обновляем подмодуль до последней версии ветки и переносим наши коммиты поверх
git submodule update --remote --rebase libs/mylib

# Входим в подмодуль при необходимости решаем конфликты
cd libs/mylib
# ... разрешаем конфликты, выполняем git rebase --continue ...
cd ..

# Фиксируем обновленное состояние подмодуля
git add libs/mylib
git commit -m "Слияние локальных правок с обновленной версией подмодуля"

Заключение

Команда git submodule update — это ключевой инструмент при работе с подмодулями. Она не "искрещивает" миры, а просто делает следующее: приводит рабочие копии подмодулей к тем коммитам, которые зафиксированы в основном репозитории.

Если обобщить:

  • без флагов команда синхронизирует подмодули с текущим коммитом основного проекта;
  • --init упрощает начальную настройку;
  • --recursive полезен, когда есть вложенные подмодули;
  • --remote позволяет отслеживать ветки подмодулей, а не конкретные коммиты;
  • --merge и --rebase помогают аккуратно обновлять подмодули при наличии локальных изменений.

Главная идея: состояние подмодуля в сборке должно однозначно определяться коммитом основного репозитория. Поэтому, обновляя подмодуль, не забывайте фиксировать изменения в основном проекте. Тогда другие разработчики смогут воспроизводить вашу среду одной командой: git submodule update.


Частозадаваемые технические вопросы по теме

Вопрос 1. Как удалить подмодуль и чтобы git submodule update больше его не трогал

  1. Удалите запись из .gitmodules.
  2. Удалите запись о подмодуле из конфигурации Git:
    • выполните команду
      git config -f .git/config --remove-section submodule.pathкподмодулю
      (подставьте правильный путь).
  3. Удалите сам каталог подмодуля:
    • rm -rf pathкподмодулю.
  4. Удалите ссылку на подмодуль из индекса:
    • git rm --cached pathкподмодулю.
  5. Сделайте коммит с этими изменениями.
    После этого git submodule update больше не будет пытаться работать с этим путем.

Вопрос 2. Почему git submodule update ничего не делает после изменения .gitmodules

Если вы только добавили подмодуль в .gitmodules или изменили его URL, но не выполнили git submodule init или не добавили файл .gitmodules в индекс и не закоммитили его, Git может "не видеть" изменений. Сделайте:

  1. git add .gitmodules.
  2. git commit -m "Обновлена конфигурация подмодулей".
  3. Затем выполните git submodule update --init --recursive.
    Теперь Git возьмет информацию из .gitmodules и корректно инициализирует и обновит подмодуль.

Вопрос 3. Как сделать так, чтобы CI всегда подтягивал подмодули правильно

Обычно в скриптах CI используют следующую последовательность:

  1. git submodule sync --recursive
    Синхронизирует локальные URL подмодулей с .gitmodules.
  2. git submodule update --init --recursive
    Инициализирует отсутствующие подмодули и обновляет все уровни.

Эти две команды делают пайплайн устойчивым к изменениям URL и добавлению новых подмодулей.

Вопрос 4. Что делать, если подмодуль указывает на несуществующий коммит

Такое случается, если:

  • кто-то в истории переписал подмодуль (rebase, force push);
  • основной репозиторий указывает на коммит, которого больше нет в удаленном репозитории подмодуля.

При git submodule update вы получите ошибку, что объект не найден. Решение:

  1. Уточните у команды, доступен ли этот коммит в каком-нибудь другом remote.
  2. Если нет, переключите подмодуль на ближайший подходящий коммит или ветку.
  3. Зафиксируйте новый коммит подмодуля в основном репозитории:
    git add pathкподмодулю и git commit.

После этого git submodule update будет работать с новым валидным коммитом.

Вопрос 5. Можно ли запретить git submodule update изменять определенный подмодуль

Напрямую "запретить" нельзя, но можно добиться похожего эффекта:

  1. Исключите подмодуль из автоматических скриптов, явно не указывая его в командах.
  2. Если вы не хотите, чтобы подмодуль обновлялся при git submodule update без аргументов, держите этот подмодуль в отдельной ветке или используйте отдельные инструкции для его обновления.
  3. Для локальной работы можно временно "отвязать" каталог подмодуля, переименовав его или удалив запись о нем из .git/config, но это скорее обходной путь и использовать его стоит аккуратно.
Статус подмодулей - git submodule statusСтрелочка вправо

Постройте личный план изучения Git до уровня Middle — бесплатно!

Git — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Git

Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы Git

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

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