Git
submodules
repository
version control
command line

List submodules in a Git repository

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Git submodules let one repository track another repository at a specific commit. Listing submodules correctly is important for audits, CI checks, and troubleshooting inconsistent checkouts. The most useful commands show not only submodule paths but also commit pinning and initialization state.

What Git Stores for Submodules

Submodule metadata appears in two places:

  • '.gitmodules stores mapping of path to URL'
  • the main repository index stores the pinned commit for each submodule

A submodule entry in Git tree uses special mode 160000, which points to a commit in the nested repository.

Understanding this structure helps explain why submodules can appear present in config but missing locally until initialized.

Primary Command: git submodule status

Use this as the default listing command.

bash
git submodule status

Typical output includes:

  • commit hash
  • submodule path
  • optional branch hint or status marker

Status markers matter:

  • leading - means submodule not initialized
  • leading + means checked out commit differs from recorded index
  • leading space means clean and matching recorded commit

Recursive Listing for Nested Submodules

If submodules themselves contain submodules, add recursive flag.

bash
git submodule status --recursive

This is essential in monorepo-like structures where dependency trees are deep.

List Paths and URLs from .gitmodules

When you need configuration-level inventory:

bash
git config --file .gitmodules --get-regexp '^submodule\..*\.(path|url)$'

This prints submodule names, paths, and URLs even if not initialized locally.

Use this in CI checks that validate expected remotes.

Enumerate Submodule Revisions Programmatically

To inspect pinned commits from the main repository tree:

bash
git ls-tree HEAD

Look for entries with mode 160000. That indicates submodule commit references tracked by the main repository.

This is useful when investigating why two branches pin different submodule revisions.

Run Commands Inside Each Submodule

For auditing branch names or detached HEAD state:

bash
git submodule foreach 'echo "$name $path"; git rev-parse --short HEAD; git branch --show-current || true'

This gives an operational view across all submodules and highlights detached checkouts.

If listing shows uninitialized submodules, initialize and update:

bash
git submodule update --init --recursive

After that, run git submodule status --recursive again to verify state.

For URL changes in .gitmodules, sync local config:

bash
git submodule sync --recursive

Then update as needed.

CI Validation Example

A simple CI guard can fail builds if submodules are out of sync.

bash
1#!/usr/bin/env bash
2set -euo pipefail
3
4out="$(git submodule status --recursive)"
5echo "$out"
6
7if echo "$out" | grep -E '^[+-]'; then
8  echo "Submodule state is not clean"
9  exit 1
10fi

This prevents accidental commits with drifting submodule pointers.

Troubleshooting Mismatch Cases

If submodule status shows +, you likely have local submodule checkout different from recorded commit. Fix by updating to recorded revision:

bash
git submodule update --recursive

If teams intentionally track moving branches during development, document that workflow clearly and avoid mixing it with strict pinning workflows in release branches.

Auditing Changes Across Branches

When release behavior differs between branches, compare submodule pointers directly in Git history. Running git log --submodule or reviewing commit diffs helps identify when a submodule pointer changed and why. This is useful for root-cause analysis when dependency behavior changes without obvious code differences in the main repository.

Common Pitfalls

  • Assuming .gitmodules alone proves submodules are initialized locally.
  • Ignoring status markers in git submodule status output.
  • Forgetting recursive flags when nested submodules exist.
  • Committing main repo changes without committing intended submodule pointer updates.
  • Running CI without validating submodule sync and initialization state.

Summary

  • Use git submodule status as the primary listing command.
  • Use recursive variants for nested dependency trees.
  • Inspect .gitmodules for path and URL configuration inventory.
  • Validate status markers to catch initialization and drift problems.
  • Add CI checks so submodule state stays consistent across environments.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.