Git Submodules Explained With Practical Examples
A Git submodule lets one repository include another repository at a specific commit. It can be useful for shared libraries, themes, or separately maintained components, but its extra repository boundary means a normal clone or commit does not behave quite like a regular folder. This guide explains the model and walks through common submodule workflows.
Practice Git workflows →What is a Git submodule?
A submodule is a separate Git repository checked out inside a parent repository. The parent records the submodule's repository URL in .gitmodules and stores a reference to one specific commit, often called a gitlink. It does not record every file in the submodule as ordinary parent-repository files.
That pointer makes builds reproducible: a parent commit selects a known submodule commit rather than automatically taking whatever is newest. The trade-off is that collaborators must initialize the submodule, and a change inside it and a change to its parent pointer are separate commits in separate repositories.
How to add a submodule to a repository
From the root of your parent repository, add the submodule with its repository URL and the path where you want it checked out:
git submodule add https://github.com/OWNER/REPOSITORY.git external/exampleReplace the example URL and path with the actual repository and desired location. Git creates or updates .gitmodules, checks out the repository under external/example, and records its current commit in the parent repository.
Review and commit both the configuration and the pointer from the parent repository:
git status
git add .gitmodules external/example
git commit -m "Add example submodule"How to clone a repository that has submodules
The simplest way to clone a parent repository and initialize all nested submodules is:
git clone --recurse-submodules https://github.com/OWNER/PROJECT.gitIf you already cloned without that option, initialize the configured submodules afterward:
git submodule update --init --recursiveInitialization fetches and checks out the commits recorded by the parent. A submodule may appear on a detached HEAD; this is normal when the parent pins a commit rather than a moving branch. Avoid committing new work there unless you deliberately create or switch to a branch in the submodule first.
How to update a submodule and the parent pointer
To move a submodule to a newer commit, first work inside the submodule. For example, fetch and fast-forward its main branch if that is the branch the project intends to follow:
git -C external/example fetch origin
git -C external/example switch main
git -C external/example pull --ff-onlyIf the submodule has no local main branch, create one that tracks the remote branch with git -C external/example switch --track -c main origin/main. Use the branch name your dependency actually maintains.
Check the submodule's status and test the parent project. Then return to the parent repository and commit the new pointer:
git status
git add external/example
git commit -m "Update example submodule"The parent commit records the selected submodule commit, not a promise to follow the branch automatically. Teammates receive the new pointer when they pull the parent and run git submodule update --init --recursive.
Common Git submodule issues
- The directory is empty. Initialize submodules with
git submodule update --init --recursive. - The parent shows a changed path. A commit inside the submodule moved its checked-out commit. Review that change; stage the path in the parent only if the new pointer is intended.
- The submodule is on detached HEAD. This is expected for a pinned commit. Create or switch to a branch inside the submodule before making a commit you intend to keep.
- A collaborator cannot clone it. Check that the URL in
.gitmodulesis correct and that the collaborator has access to the submodule repository.
Git submodules FAQ
Do submodules automatically track the latest commit?
No. The parent repository records a specific submodule commit. Updating a submodule changes the pointer, and that change must be reviewed and committed in the parent.
Are submodules the same as copying a folder?
No. The submodule keeps its own Git history and remote, while the parent stores a commit reference and configuration. If you need a single repository with all files and history together, consider whether a monorepo or another dependency-management approach better fits the project.