git submodule: клонировал, а папка пустая
Обновлено 31 июля 2026 г.
Подмодуль (submodule) - это ссылка на другой репозиторий, вложенный внутрь твоего. В истории он хранится не как файлы, а как хеш одного конкретного коммита чужого проекта. Поэтому обычный git clone забирает только ссылку, а сами файлы подмодуля остаются на сервере.
Симптом узнаётся сразу: склонировал проект, зашёл во вложенную папку, а она пустая.
$ git clone https://github.com/username/some-project.git
Cloning into 'some-project'...
...
$ cd some-project
$ ls vendor/lib
$Папка есть, файлов нет. Git знает про подмодуль, но не тронул его:
$ git submodule status
-e2f3a4b vendor/libМинус в начале строки значит «подмодуль не инициализирован»: git видит записанный хеш, но содержимое не скачал. Что это за ссылка, записано в файле .gitmodules в корне проекта:
$ cat .gitmodules
[submodule "vendor/lib"]
path = vendor/lib
url = https://github.com/other/lib.gitКак забрать файлы
Клонируешь с нуля - добавь флаг --recurse-submodules, и git сходит за подмодулями тоже:
$ git clone --recurse-submodules https://github.com/username/some-project.git
...
Submodule 'vendor/lib' (https://github.com/other/lib.git) registered for path 'vendor/lib'
Submodule path 'vendor/lib': checked out 'e2f3a4b'Проект уже склонирован, а папка пустая - доскачай одной командой:
$ git submodule update --init
Submodule path 'vendor/lib': checked out 'e2f3a4b'--init в первый раз обязателен: без него git не знает, что подмодуль надо развернуть.
Почему diff показывает хеш, а не строки
Правишь код внутри подмодуля - и git diff во внешнем проекте показывает не строки, а сдвиг одного хеша:
$ git diff
diff --git a/vendor/lib b/vendor/lib
index e2f3a4b..a1b2c3d 160000
--- a/vendor/lib
+++ b/vendor/lib
@@ -1 +1 @@
-Subproject commit e2f3a4b5...
+Subproject commit a1b2c3d4...Режим 160000 - это и есть подмодуль-ссылка. Внешний репозиторий хранит только то, на каком коммите стоит вложенный; строки живут в истории самого подмодуля.
Частые ошибки
Забыть --recurse-submodules при клонировании - и получаешь ту самую пустую папку. Обманчиво тут то, что падает не git, а сборка позже, когда компилятор не находит чужой код. Лечится тем же git submodule update --init.
Правки внутри подмодуля коммитятся в его собственную историю, а не во внешний проект. Забудешь запушить сам подмодуль - у коллеги ссылка будет указывать на коммит, которого на сервере нет.