git submodule: cloned it, and the folder is empty
Updated July 31, 2026
A submodule is a link to another repository nested inside yours. In the history it is stored not as files, but as the hash of one specific commit of that other project. So a plain git clone takes only the link, while the submodule's own files stay on the server.
The symptom is easy to spot: you clone a project, step into the nested folder, and it is empty.
$ git clone https://github.com/username/some-project.git
Cloning into 'some-project'...
...
$ cd some-project
$ ls vendor/lib
$The folder is there, the files are not. Git knows about the submodule but has not touched it:
$ git submodule status
-e2f3a4b vendor/libThe minus at the start of the line means "submodule not initialized": git sees the recorded hash but has not downloaded the contents. What that link points to is written in the .gitmodules file at the project root:
$ cat .gitmodules
[submodule "vendor/lib"]
path = vendor/lib
url = https://github.com/other/lib.gitHow to get the files
Cloning from scratch, add the --recurse-submodules flag and git goes after the submodules too:
$ 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'Project already cloned and the folder empty - pull the submodules in with one command:
$ git submodule update --init
Submodule path 'vendor/lib': checked out 'e2f3a4b'--init is required the first time: without it git does not know the submodule should be laid out.
Why diff shows a hash, not lines
Edit code inside the submodule, and git diff in the outer project shows not lines but the shift of a single hash:
$ 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...The 160000 mode is the submodule link itself. The outer repository stores only which commit the nested one sits on; the lines live in the submodule's own history.
Common mistakes
Forget --recurse-submodules while cloning and you get that same empty folder. The catch is that it fails not on git but on the build later, when the compiler cannot find the other project's code. The cure is the same git submodule update --init.
Edits inside the submodule commit to its own history, not to the outer project. Forget to push the submodule itself, and your teammate's link will point at a commit that is not on the server.