Git’s submodule
tool allows you to create a child repository as a submodule inside a repository. This article shows you how to manipulate a submodule.
Add a submodule from a remote repository
To create a new sub repository whose content has not existed in the repository:
# Add a submodule named a-submodule from an remote repository
# This command will create a .gitmodules which contains the mapping relation of
# the submodule and its remote repository.
# It also stages the changes.
$ git submodule add https://github.com/xxx/a-submodule
# Use status to check what the above command stages
$ git status
...
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: a-submodule
# You need to commit staged changes: .gitmodules and a-submodule
$ git commit -m 'add a submodule'
[master 9d7d3b3] add: add a submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 a-submodule
As you see, Git treats the new submodule as a file with special mode (1600000) in a commit.
For a repository with submodules inside it, it is called the main repository.
Note: In new git versions, the submodule’ s
.git
data is stored the main repository’s.git
directory while they stay in the submodule’s directory in the old versions.
Add a submodule from an subdirectory
You can also switch a subdirectory to a submodule. To add a submodule from a subdirectory that has been tracked, you need to remove it first:
# First remove the subdirectory named csvlib from index
$ git rm --cached csvlib
Then add a subdirectory as a submodule:
# Init csvlib as a repo
$ cd csvlib
$ git init
$ git add .
$ git commit -m 'initial commit'
# Add csvlib repo as a submodule
$ git submodule add ./csvlib/
Adding existing repo at 'csvlib' to the index
# Check status
$ git status
...
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: .gitmodules
new file: csvlib
deleted: csvlib/csv.py
# Commit the changes
$ git commit -m 'switch subdirectory csvlib as a submodule'
[master 9d7d3b3] add: add submodule git-submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 a-submodule
Note: If you switch to a branch in which the subdirectory not being as a submodule,you may get an error for the subdirectory will be overwritten. At this time, an option
-f
is needed to checkout. Afterwards if you get back to the branch treating it as a submodule, you need to executegit checkout .
in the submodule’s directory to get the content back. See more on git submodules.
Clone a repository with submodules
If you clone a repository with submodules inside it as you do usually like below, you will get empty directories for submodules.
$ git clone <repository url>
To fetch the files in the submodules, you need to run two more commands:
# Initialize local configuration file
$ git submodule init
# Fetch files of submodules
$ git submodule update
Cloning into '<submodule name>'...
If there are nested submodules, add --recursive
option to these two commands. You can also combine the two commands into one:
# Initialize and update submodules (with nested ones) with one command
$ git submodule update --init --recursive
Or you can add an --recurse-submodules
option to the git clone
command to automatically initialize and fetch each submodule recursively.
# Clone a repository with submodules
$ git clone --recurse-submodules <repository url>
Commit changes for a submodule
To commit changes in a submodule, first commit in its own repository and then commit for the main repository:
# Commit changes in the submodule directory
$ git add a.py
$ git commit -m 'feat: add some functionly'
# Push changes to its remote repository
$ git push
# Switch to the main repository directory
$ cd ..
# Check changes from a submodule
$ git status
...
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
(commit or discard the untracked or modified content in submodules)
modified: a-submodule (modified content)
# Commit the submodule's changes to the top repository
$ git add a-submodule
$ git commit -m 'update changes of submodule a-submodule'
# Push changes if you want
$ git push
Pull updates for a submodule
To pull updates from remote for a submodule, an easy way is to run:
# Let Git to go into the submodule's directory and get updates for a-submodule
# --merge, merge local work
$ git submodule update --remote --merge a-submodule
Or you can do it manually:
# Switch to the submodule's directory
$ cd a-submodule
# Pull and merge updates form upstream branch
$ git pull
$ git merge origin/master
Manipulate multiple submodules at a time
Git has a submodule foreach
command for you to manipulate multiple submodules. For example, stashing changes for all your submodules:
# Stash changes for all submodules
$ git submodule foreach 'git stash'
List all the submodules in a repository
Git does not provide a command something like git submodule list
to list all the submodules. Since all the submodules in a repository are recorded in the .gitmodules
file. Check that file to get all the submodules. You can do that with a command:
# Output the content of .gitmodules
$ cat .gitmodules
Or you can do this with help with git submodule status
which outputs some information for each submodule:
$ git submodule status
e8aecd...ebd a-submodule-name (heads/main)
...
Note if you only care about changes of each submodule, use git status
instead.
Remove a submodule from a repository
To remove a submodule named a-submodule from a repository:
# Frist remove the from index
$ git rm --cached a-submodule
# If you want to keep submodule's files in working tree.
# Rename it for the next command will delete it from working tree
$ mv a-submodule a-submodule-renamed
# Unregister the submodule
# This command removes it both from .git/config and working tree
$ git submodule deinit a-submodule