How can I specify a branch or tag when adding a Git submodule?
How does git submodule add -b work?
After adding a submodule with a specific branch, a new cloned repository (after git submodule update --init) will be at a specific commit, not the branch itself (git status on the submodule shows “Not currently on any branch”).
I can’t find any information on .gitmodules or .git/config about the submodule’s branch or any specific commit, so how does Git figure it out?
Also, is it possible to specify a tag instead of a branch?
When adding a Git submodule, you can specify a branch using the -b or --branch option, but tags require a different approach. The submodule tracks specific commits rather than branches or tags directly, which explains why you see “Not currently on any branch” status.
Contents
- Specifying a Branch with git submodule add -b
- How git submodule add -b Works
- Understanding the Underlying Mechanism
- Specifying a Tag Instead of a Branch
- Practical Examples
- Best Practices
Specifying a Branch with git submodule add -b
You can specify a branch when adding a Git submodule using the -b or --branch option:
git submodule add -b <branch_name> <repository_url> <path/to/submodule>
Or the longer form:
git submodule add --branch <branch_name> <repository_url> <path/to/submodule>
For example:
git submodule add -b master https://github.com/buildroot/buildroot git submodule add --branch development https://github.com/example/example-repo submodules/example-repo
The -b option is only valid for the add, deinit and update commands according to the official Git documentation source.
How git submodule add -b Works
When you use git submodule add -b <branch_name>, here’s what actually happens:
- Git adds the submodule as usual, checking out the latest commit from the specified branch
- Git records the specific commit hash of that branch tip in the parent repository
- The
.gitmodulesfile is updated to include the branch name for future reference - When someone clones the repository and runs
git submodule update --init, they get the exact commit that was recorded, not the branch tip
This is why after cloning and updating submodules, you see “Not currently on any branch” - because you’re on a detached HEAD state at the specific commit that was recorded when the submodule was first added source.
Understanding the Underlying Mechanism
The confusion about Git’s submodule tracking stems from understanding how commits are stored and referenced:
.gitmodules File
The .gitmodules file can contain branch information but not commit hashes or tags:
[submodule "example-repo"]
path = submodules/example-repo
url = https://github.com/example/example-repo
branch = master # This is supported
# SHA and TAG are NOT supported in .gitmodules
How Git Tracks Submodules
Git tracks submodules through:
- Commit hashes stored in the parent repository’s Git object database
- Branch names stored in
.gitmodulesfor reference and future updates - Configuration in
.git/configthat takes precedence over.gitmodules
When you run git submodule add -b master, Git:
- Checks out the tip of the
masterbranch - Records the commit hash of that tip
- Stores
branch = masterin.gitmodulesfor future reference - When updating, it can use the branch name to find the latest commit, but by default uses the recorded commit hash source.
Specifying a Tag Instead of a Branch
Tags cannot be specified directly in the git submodule add command, but here are the workarounds:
Method 1: Two-Step Process
# 1. Add the submodule without specifying a branch
git submodule add https://github.com/example/example-repo submodules/example-repo
# 2. Navigate to submodule and checkout the tag
cd submodules/example-repo
git checkout tags/v1.0.0
cd ../../
# 3. Commit the change
git add submodules/example-repo
git commit -m "Checked out tag v1.0.0 for submodule"
Method 2: Using Commit Hash
If you know the specific commit hash associated with the tag:
git submodule add https://github.com/example/example-repo submodules/example-repo
cd submodules/example-repo
git checkout <commit_hash>
cd ../../
git add submodules/example-repo
git commit -m "Fixed submodule to specific commit"
Important Limitation
As noted in the Stack Overflow answer, only branch name is supported in a .gitmodules file, but SHA and TAG are not supported source.
Practical Examples
Example 1: Adding Submodule with Specific Branch
# Add submodule tracking the 'feature' branch
git submodule add -b feature https://github.com/user/repo.git libs/repo
# This records the commit that was at the tip of 'feature' branch
# when you ran the command
Example 2: Adding Submodule with Specific Tag
# Add submodule without branch specification
git submodule add https://github.com/user/repo.git libs/repo
# Navigate to submodule and checkout tag
cd libs/repo
git checkout tags/v2.1.0
cd ../..
# Commit the specific commit
git add libs/repo
git commit -m "Added submodule at v2.1.0 tag"
Example 3: Updating Submodule to Track Branch
For an existing submodule, you can set it to track a specific branch:
# Set the submodule to track a specific branch
git submodule set-branch -b feature libs/repo
# Update the submodule to the latest from that branch
git submodule update --remote libs/repo
Best Practices
- Use branches for active development: When you want submodules to track ongoing development
- Use tags/commits for stable releases: When you need pinned versions
- Document your choices: Make it clear in your README which approach you’re using
- Be consistent: Use the same approach across your project
- Consider alternatives: For some use cases, Git submodules might not be the best choice - consider Git LFS, package managers, or monorepo approaches
Remember that submodules track specific commits, not branches or tags directly. The branch specification is mainly for convenience when updating submodules later source.
Sources
- How can I specify a branch/tag when adding a Git submodule? - Stack Overflow
- Git - git-submodule Documentation
- How To Specify A Branch/Tag When Adding a Git Submodule? - GeeksforGeeks
- git - What’s the benefit of specifying a branch for a submodule? - Super User
- How to specify a branch/tag when adding a Git submodule - Educative
Conclusion
- Use
-bor--branchoption withgit submodule addto specify a branch, but understand this only records the commit hash at the branch tip - Submodules always track specific commits, not branches or tags directly
- Tags require a two-step process: add the submodule, then checkout the tag within the submodule
- The
.gitmodulesfile stores branch names but not commit hashes or tags - Git tracks submodule commits through the parent repository’s Git object database
- For stable releases, consider using tags/commits; for active development, branches may be more convenient
When working with submodules, always remember that you’re working with specific commits, not the abstract concepts of branches or tags. This design choice gives you precise control but requires understanding how Git tracks submodule references.