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.

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

Где это в учебнике

Глава 6. GitHub: облако и совместная работа.