Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to split submodules in Git

Splitting a Git submodule into multiple smaller submodules helps organize large codebases and manage dependencies more granularly. As the creator of CoreUI with over 25 years of version control experience since 2000, I’ve restructured numerous projects where monolithic submodules needed division for better maintainability. The recommended approach uses git filter-branch or git subtree to extract subdirectories into new repositories, then replaces the original submodule with multiple smaller ones. This preserves commit history while improving project organization.

Use git filter-branch to extract a subdirectory into a new repository.

# Clone the submodule repository
git clone https://github.com/username/large-submodule.git submodule-part1
cd submodule-part1

# Extract only the subdirectory with history
git filter-branch --subdirectory-filter path/to/part1 -- --all

# Create new remote repository and push
git remote set-url origin https://github.com/username/submodule-part1.git
git push -u origin main

The filter-branch --subdirectory-filter rewrites history to include only the specified directory. All commits touching that directory are preserved. The directory becomes the repository root. This creates a new repository with full history for just that subdirectory.

Splitting Using git subtree split

Modern alternative to filter-branch using subtree commands.

# In the original submodule repository
git clone https://github.com/username/large-submodule.git
cd large-submodule

# Split out the subdirectory
git subtree split --prefix=path/to/part1 -b part1-branch

# Create new repository
cd ..
mkdir submodule-part1
cd submodule-part1
git init
git pull ../large-submodule part1-branch

# Push to new remote
git remote add origin https://github.com/username/submodule-part1.git
git push -u origin main

The git subtree split creates a branch containing only commits affecting the specified prefix. The pull imports that branch into a new repository. This is faster than filter-branch and recommended for large repositories.

Replacing Original Submodule with Split Versions

Update the parent repository to use the new split submodules.

# In the parent repository
cd parent-repo

# Remove the original submodule
git submodule deinit -f libs/large-submodule
git rm -f libs/large-submodule
rm -rf .git/modules/libs/large-submodule

# Add the split submodules
git submodule add https://github.com/username/submodule-part1.git libs/part1
git submodule add https://github.com/username/submodule-part2.git libs/part2
git submodule add https://github.com/username/submodule-part3.git libs/part3

# Initialize the new submodules
git submodule update --init --recursive

# Commit the changes
git add .gitmodules libs/
git commit -m "Split large-submodule into part1, part2, and part3"

The original submodule is removed completely. Each split part is added as a new submodule. The .gitmodules file is updated automatically. This restructures the project with granular submodules.

Splitting by Functionality

Organize splits based on logical functionality rather than directory structure.

# Example: splitting a utilities submodule
# Original structure:
# utils/
#   ├── date/
#   ├── string/
#   ├── array/
#   └── validation/

# Split into separate repositories
cd utils

# Create date-utils repository
git subtree split --prefix=date -b date-utils-branch
mkdir ../date-utils && cd ../date-utils
git init
git pull ../utils date-utils-branch
git remote add origin https://github.com/username/date-utils.git
git push -u origin main

# Repeat for string-utils, array-utils, validation-utils

Each functional area becomes an independent repository. This allows versioning libraries separately. Teams can update individual utilities without affecting others. Dependencies become more explicit.

Handling Shared Code During Split

Duplicate shared files into both split repositories when needed.

# If both split parts need common code
cd large-submodule

# For part1, include common files
git subtree split --prefix=part1 -b part1-branch

# Manually merge common files into part1
cd ../submodule-part1
git checkout part1-branch
cp ../large-submodule/common/* ./
git add .
git commit -m "Add shared common files"
git push

When subdirectories share code, either extract shared code to a third submodule or duplicate it. Duplication is simpler for small shared files. For substantial shared code, create a separate common submodule that both parts depend on.

Verifying Split Integrity

Ensure all history and files were preserved correctly.

# Check commit history is intact
cd submodule-part1
git log --oneline
git log --stat

# Verify no files were lost
ls -la
git ls-files

# Check total commits match expected
git rev-list --count main

# Compare with original submodule commits for that path
cd ../large-submodule
git log --oneline -- path/to/part1 | wc -l

The commit count should match the number of commits touching that subdirectory. All files that existed in the subdirectory should be present. The git log --stat shows file changes in each commit to verify integrity.

Updating Dependencies After Split

Update code references to account for new submodule locations.

# In the parent repository after splitting
cd parent-repo

# Update import paths in code
# Before: import { util } from './libs/large-submodule/part1/util'
# After:  import { util } from './libs/part1/util'

# Find and update all references
grep -r "large-submodule/part1" src/
# Manually update each file

# Test that everything still works
npm test

# Commit the import path changes
git add src/
git commit -m "Update imports after submodule split"

Code importing from the submodule needs path updates. Build scripts or configuration files may reference submodule paths. Test thoroughly to ensure nothing broke. Update documentation to reflect the new structure.

Best Practice Note

This is the same submodule splitting process we follow when restructuring CoreUI projects to separate concerns and improve maintainability. Before splitting, document why the split is necessary and plan the new structure carefully. Consider whether Git submodules are the best solution - npm packages or monorepo tools like Nx might be simpler. Always backup the repository before filter-branch operations as they rewrite history. Communicate splits to the team in advance as everyone needs to update their clones. For large repositories, use git subtree split instead of filter-branch as it’s significantly faster. After splitting, archive the original monolithic submodule repository rather than deleting it to preserve full history. For more about managing submodules, see our guides on how to sync submodules and how to remove submodules.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author