git submodule: Usage, Options & Common CI Errors
git submodule embeds another repository at a fixed commit inside your repo.
Submodules are not fetched by a plain clone. CI must init and update them, often with private-repo auth.
What it does
git submodule tracks external repositories as nested checkouts pinned to a specific commit, recorded in .gitmodules and the parent tree.
Common usage
git submodule update --init --recursive
git submodule sync --recursive # apply URL changes
git submodule add https://github.com/owner/lib.git libs/lib
git submodule status
git clone --recurse-submodules <url>Options
| Subcommand / flag | What it does |
|---|---|
| update --init | Initialize and fetch submodules |
| --recursive | Recurse into nested submodules |
| sync | Update submodule URLs from .gitmodules |
| --remote | Update to the latest upstream commit |
| add <url> <path> | Add a new submodule |
Common errors in CI
Empty submodule directories mean update --init was never run. fatal: could not read Username for a submodule means a private submodule needs credentials - provide a token or use insteadOf URL rewriting. In actions/checkout, set submodules: recursive and a token with access.
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached