How to work with submodules in Git
Managing dependencies on external Git repositories requires a way to include them in your project while keeping their history separate. With over 25 years of experience in software development and as the creator of CoreUI, I’ve used Git submodules in numerous projects to manage shared libraries, themes, and component packages. From my expertise, the most reliable approach is to use Git’s built-in submodule feature, which allows you to keep a Git repository as a subdirectory of another repository. This method maintains separate version control for each component while integrating them into your main project.
Use git submodule add to include an external repository as a subdirectory in your project.
git submodule add https://github.com/coreui/coreui.git libs/coreui
How It Works
The git submodule add command clones the external repository into the specified directory (libs/coreui) and creates a .gitmodules file that tracks the submodule’s URL and path. Git stores the submodule at a specific commit, not a branch, ensuring consistent versions across all clones of your repository. The parent repository only tracks which commit the submodule should be at.
Cloning a Repository with Submodules
When cloning a repository that contains submodules, use the --recurse-submodules flag:
git clone --recurse-submodules https://github.com/yourname/yourproject.git
This command clones both the main repository and initializes all submodules in one step. Without this flag, the submodule directories would be empty, and you would need to run git submodule init and git submodule update manually.
Initializing and Updating Submodules
If you’ve already cloned without the recursive flag, initialize and update submodules:
git submodule init
git submodule update
The init command registers the submodules from the .gitmodules file, and update clones the repositories and checks out the specific commit recorded in the parent repository. You can combine these into a single command: git submodule update --init.
Updating Submodules to Latest Version
To update a submodule to the latest commit from its tracked branch:
cd libs/coreui
git pull origin main
cd ../..
git add libs/coreui
git commit -m "chore: update coreui submodule to latest version"
Enter the submodule directory, pull the latest changes, then return to the parent repository and commit the new submodule commit reference. This updates the parent repository’s record of which commit the submodule should use.
Updating All Submodules at Once
Update all submodules to their latest commits in one command:
git submodule update --remote --merge
The --remote flag fetches the latest changes from the submodule’s remote repository, and --merge merges them into the current branch. This is useful when you have multiple submodules and want to update them all without entering each directory.
Working on Submodule Code
To make changes within a submodule, treat it as a regular Git repository:
cd libs/coreui
git checkout -b feature/new-component
# Make changes to files
git add .
git commit -m "feat: add new component"
git push origin feature/new-component
cd ../..
git add libs/coreui
git commit -m "chore: update coreui with new component"
The submodule is a full Git repository, so you can create branches, make commits, and push changes. After committing in the submodule, return to the parent repository and commit the new submodule reference to record the update.
Removing a Submodule
To remove a submodule from your project:
git submodule deinit libs/coreui
git rm libs/coreui
rm -rf .git/modules/libs/coreui
git commit -m "chore: remove coreui submodule"
The deinit command unregisters the submodule, git rm removes it from the working tree and index, and manually deleting the .git/modules directory cleans up the cached repository. This completely removes all traces of the submodule from your project.
Checking Submodule Status
View the status of all submodules in your project:
git submodule status
This command shows the commit hash, path, and any status indicators for each submodule. A minus sign (-) indicates the submodule is not initialized, a plus sign (+) means it’s at a different commit than recorded, and the letter U indicates merge conflicts.
Best Practice Note
This is the same Git submodules workflow we use in CoreUI projects to manage shared components and dependencies across multiple repositories. Submodules are powerful but can be complex - for simpler dependency management, consider using npm packages instead. Use submodules when you need direct access to the source code, want to track specific commits, or are working with repositories that aren’t distributed via package managers. Always document your submodule workflow in the project README, and consider adding Git hooks to automatically update submodules. For related Git operations, check our guide on how to add a submodule in Git for more detailed setup instructions.



