Sharing code between repositories needs a mechanism to include one inside another. A submodule records a pointer to a specific commit in the other repo; a subtree copies the other repo’s content into yours. They differ in where the history lives and how changes flow.
Before you start
You should be comfortable with cloning and commits. This article compares the two approaches.
Step-by-step walkthrough
Step 1: Submodules keep a pointer
git submodule add records the URL and a specific commit. The parent repo stores a pointer, not the files, so a clone must run git submodule update --init to populate it. The submodule has its own history and is updated by moving the pointer.
Step 2: Subtrees copy content into the parent
git subtree add merges the other repo’s history into a subdirectory of yours, so the files are present in a normal clone with no extra steps. Updates pull or push between the two with git subtree pull and git subtree push.
Step 3: Choose by ownership and change rate
Submodules suit a dependency you do not modify, whose history should stay separate, at the cost of clone and update friction. Subtrees suit shared code you sometimes modify, with a simpler clone, at the cost of duplicated history and more complex merges.
Worked scenario
A fresh clone needs an extra step for a submodule.
git clone --recurse-submodules <url>
# or, after a plain clone:
git submodule update --init --recursiveWalk through the example
The clone brings the parent repo and, with the flag, the submodule content at the recorded commit. A plain clone leaves the submodule directory empty, which is a common surprise. A subtree would already be present, because its files live in the parent history.
Common mistake
Cloning without initializing submodules and finding empty directories, or updating a submodule’s files without committing the moved pointer in the parent, so the change is lost.
Verify the behavior
Clone a repo with a submodule and confirm the directory is empty without init and populated with it. Change the submodule commit and confirm the parent shows a pointer change. For a subtree, confirm the files appear in a plain clone.
Interview exercise
Why is a submodule’s directory empty after a plain clone?
Answer and reasoning
Because the parent repository stores only a pointer to a commit in the submodule’s remote, not the files themselves. Cloning the parent does not clone the submodule, so you must run git submodule update --init (or clone with --recurse-submodules) to fetch its content at the recorded commit. A subtree embeds the files, so it needs no such step.
Continue learning
Compare vendoring in Ignore tracked files and large files in LFS. Read the Git submodules documentation and try the Git interview questions.