This status appears in 'git status' when a submodule's working directory contains uncommitted changes or has checked out a different commit than what the parent repository expects. The fix involves either updating, committing, or resetting the submodule.
When you run `git status` in your main repository and see "(modified content)" next to a submodule path, it means the submodule's working directory has changes that differ from what Git expects. This is often called a "dirty" submodule. A submodule is considered "dirty" when: - Files inside the submodule have been modified but not committed - New untracked files exist in the submodule directory - The submodule HEAD points to a different commit than what the parent repository has recorded Git tracks submodules by storing a reference to a specific commit SHA in the parent repository. When you clone or update a project with submodules, Git checks out each submodule at its recorded commit. If anything changes inside the submodule directory - whether through manual edits, running build commands that generate files, or checking out a different branch - Git will mark it as having "modified content." This behavior helps ensure that everyone working on the project uses the exact same version of each submodule, but it can be confusing when the status appears unexpectedly.
First, navigate to the submodule directory and see what Git thinks has changed:
cd path/to/submodule
git status
git diffThis shows you whether there are:
- Modified files (local edits)
- Untracked files (new files not in the submodule's .gitignore)
- A detached HEAD at a different commit
Understanding the cause helps you choose the right fix.
If you want to discard all changes and return the submodule to the exact state the parent repository expects:
git submodule update --checkoutOr for all submodules recursively:
git submodule update --checkout --recursiveThis checks out the commit SHA that the parent repository has recorded for each submodule.
If the regular update doesn't work because of local modifications, use the force flag:
git submodule update --force --recursiveOr reset changes manually inside the submodule:
cd path/to/submodule
git checkout .
git clean -fdWarning: This discards all uncommitted changes in the submodule.
To reset all submodules in your project to their clean state:
git submodule foreach --recursive git checkout .
git submodule foreach --recursive git clean -fdThis runs the checkout and clean commands inside each submodule directory.
If you intentionally updated the submodule to a new commit and want to record this in the parent repository:
# First, commit any changes inside the submodule
cd path/to/submodule
git add .
git commit -m "Your changes"
git push
# Then, update the parent repository to point to the new commit
cd ..
git add path/to/submodule
git commit -m "Update submodule to latest version"The parent repository stores only the commit reference, not the submodule's files.
If you want Git to stop showing "modified content" for submodules with local changes, add the ignore setting to your .gitmodules file:
[submodule "path/to/submodule"]
path = path/to/submodule
url = https://github.com/example/repo.git
ignore = dirtyOr set it via command line:
git config -f .gitmodules submodule.path/to/submodule.ignore dirtyOptions for ignore:
- untracked: Ignore only untracked files
- dirty: Ignore all working tree changes
- all: Ignore all changes including new commits
For a quick one-time check without seeing dirty submodules:
git status --ignore-submodules=dirtyOr to ignore all submodule changes:
git status --ignore-submodules=allYou can also set this as your default behavior:
git config --global diff.ignoreSubmodules dirtySometimes "modified content" appears when there's an accidental nested Git repository. Check if there's a .git folder inside a regular directory:
find . -name ".git" -type dIf you find unexpected .git directories that aren't proper submodules, you may need to either:
- Remove the nested .git folder and add the files normally
- Convert the directory to a proper submodule using git submodule add
Understanding Submodule State Indicators:
When you run git submodule status, the prefix character tells you the state:
- (space): Submodule is at the expected commit
- +: Submodule is checked out to a different commit
- -: Submodule is not initialized
- U: Submodule has merge conflicts
Detached HEAD in Submodules:
By default, git submodule update checks out submodules in "detached HEAD" state, pointing to the specific commit recorded in the parent repository. This is intentional - it ensures reproducible builds. If you see "HEAD detached at <commit>" when entering a submodule, this is normal behavior, not an error.
CI/CD Considerations:
In CI/CD pipelines, always use:
git submodule update --init --recursiveThis ensures all submodules are properly initialized and checked out to their expected commits, avoiding "modified content" issues during builds.
Line Ending Issues:
If you're working across Windows and Unix systems, line ending differences can cause submodules to appear dirty. Configure Git to handle this:
git config --global core.autocrlf input # On Unix/Mac
git config --global core.autocrlf true # On WindowsFilemode Changes:
On some systems (especially when sharing files between Windows and WSL), file permission changes can mark submodules as dirty. Disable filemode tracking if this is causing issues:
cd path/to/submodule
git config core.filemode falsewarning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server