How do I remove a Git submodule?
Why can't I do
git submodule rm module_name?
How do I remove a Git submodule?
Why can't I do
git submodule rm module_name?
In modern git (I'm writing this in 2022, with an updated git installation), this has become quite a bit simpler:
git rm <path-to-submodule>, and commit.This removes the filetree at <path-to-submodule>, and the submodule's entry in the .gitmodules file. I.e. all traces of the submodule in your repository proper are removed.
As the docs note however, the .git dir of the submodule is kept around (in the modules/ directory of the main project's .git dir), "to make it possible to checkout past commits without requiring fetching from another repository".
If you nonetheless want to remove this info, manually delete the submodule's directory in .git/modules/, and remove the submodule's entry in the file .git/config. These steps can be automated using the commands
rm -rf .git/modules/<path-to-submodule>, andgit config --remove-section submodule.<path-to-submodule>.Older community wiki instructions:
Via the page Git Submodule Tutorial:
To remove a submodule you need to:
.gitmodules file..gitmodules changes:git add .gitmodules.git/config.git rm --cached path_to_submodule (no trailing slash)..git directory:rm -rf .git/modules/path_to_submodulegit commit -m "Removed submodule <name>"rm -rf path_to_submoduleSee also: alternative steps below.
You must remove the entry in .gitmodules and .git/config, and remove the directory of the module from the history:
git rm --cached path/to/submodule
If you'll write on git's mailing list probably someone will do a shell script for you.
git submodule deinit <path to submodule>.gitmodulesgit rm <path to submodule>git add .gitmodulesFor the benefit of the reader, this here tries to sum it up and give a step-by-step guide on how to do it if things do not work as expected. Following is the tested and safe way for git version 2.17 and above to get rid of a submodule:
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
2.20.1 and Ubuntu 18.04 2.17.1."$submodule" is just to emphasize where to put the name, and that you have to be careful with spaces and the like"$submodule" with the Windows way of a properly specified path to the submodule. (I am not Windows)Warning!
Never touch the insides of the
.gitdirectory yourself! Editing inside.gitenters the dark side. Stay away at all cost!And yes, you can blame
gitfor this, as many handy things were missing ingitin the past. Like a proper way to remove submodules again.I think there is a very dangerous part in the documentation of
git submodule. It recommends to remove$GIT_DIR/modules/<name>/yourself. In my understanding this is not only plain wrong, it is extremely dangerous and provokes major headaches in future! See below.
Note that
git module deinit
is the direct inverse to
git module init
but
git submodule deinit -- module
git rm -- module
also is quite the inverse to
git submodule add -- URL module
git submodule update --init --recursive -- module
because some commands basically need to do more than just a single thing:
git submodule deinit -- module
.git/configgit rm
.gitmodulesgit submodule add
.git/modules/NAME/git submodule init, so updates .git/configgit submodule update, so, nonrecursively checks out the module.gitmodulesgit submodule update --init --recursive -- module
This cannot be fully symmetric, as keeping it strictly symmetric does not make much sense. There simply is no need for more than two commands. Also "pulling in the data" is implicit, because you need it, but removing the cached information is not done, because this is not needed at all and might wipe precious data.
This truly is puzzling to newcomers, but basically is a good thing: git just does the obviously thing and does that right, and does not even try to do more. git is a tool, which must do a reliable job, instead of being just another "Eierlegende Wollmilchsau" ("Eierlegende Wollmilchsau" translates for me to "some evil version of a Swiss army knife").
So I understand complaints of people, saying "Why doesn't do git the obvious thing for me". This is because "obvious" here depends from the point of view. Reliability in each and every situation is far more important. Hence what's obvious for you often is not the right thing in all possible technical situations. Please remember that: AFAICS git follows the technical path, not the social one. (Hence the clever name: git)
The commands above may fail due to following:
git is too old. Then use a newer git. (See below how to.)git clean sense. Then first clean your submodule using that command. (See below.)git. Then you are on the dark side and things get ugly and complicated. (Perhaps using another machine fixes it.)git power-user.)Possible fixes follow.
gitIf your machine is too old there is no submodule deinit in your git. If you do not want (or can) update your git, then just use another machine with a newer git! git is meant to be fully distributed, so you can use another git to get the job done:
workhorse:~/path/to/worktree$ git status --porcelain must not output anything! If it does, cleanup things first!workhorse:~/path/to/worktree$ ssh account@othermachineothermachine:~$ git clone --recursive me@workhorse path/to/worktree/.git TMPWORK && cd TMPWORKothermachine:~/TMPWORK$ git commit . -m . && exitworkhorse:~/path/to/worktree$ git fetch account@othermachine:TMPWORK/.gitworkhorse:~/path/to/worktree$ git merge --ff-only FETCH_HEAD. If this does not work, use git reset --soft FETCH_HEADgit status is clean again. You are able to do so, because you have had it clean before, thanks to the first step.This othermachine can be some VM, or some Ubuntu WSL under Windows, whatever. Even a chroot (but I assume that you are non-root, because if you are root it should be more easy to update to the newer git).
Note that if you cannot ssh in, there are trainloads of ways to transport git repositories. You can copy your worktree on some USB stick (including the .git directory), and clone from the stick. Clone the copy, just to get things in a clean fashion again. This might be a PITA, in case your submodules are not accessible from othermachine directly. But there is a solution for this, too:
git config --add url.NEWURLPREFIX.insteadOf ORIGINALURLPREFIX
You can use this multiply, and this is saved into $HOME/.gitconfig. Something like
git config --add 'url./mnt/usb/repo/.insteadof' https://github.com/
rewrites URLs like
https://github.com/XXX/YYY.git
into
/mnt/usb/repo/XXX/YYY.git
It's easy if you start to become accustomed to powerful git features like this.
Cleaning manually up is good, because this way you perhaps detect some things you forgot about.
git status and git clean -ixfd is your friendrm and deinit as long as you can. Options (like -f) for git are good if you are a Pro. But as you came here, you probably are not so experienced in the submodule area. So better be safe than sorry.Example:
$ git status --porcelain
M two
$ git submodule deinit two
error: the following file has local modifications:
two
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'two' contains local modifications; use '-f' to discard them
$ cd two
$ git submodule deinit --all
error: the following file has local modifications:
md5chk
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'md5chk' contains local modifications; use '-f' to discard them
$ cd md5chk
$ git submodule deinit --all
error: the following file has local modifications:
tino
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'tino' contains local modifications; use '-f' to discard them
$ cd tino
$ git status --porcelain
?? NEW
$ git clean -i -f -d
Would remove the following item:
NEW
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers 4: ask each
5: quit 6: help
What now> 1
Removing NEW
$ cd ../../..
$ git status --porcelain
$ git submodule deinit two
Cleared directory 'two'
Submodule 'someunusedname' (https://github.com/hilbix/src.git) unregistered for path 'two'
You see, there is no -f needed on submodule deinit. If things are clean, in a git clean sense. Also note that git clean -x is not needed. This means git submodule deinit unconditionally removes untracked files which are ignored. This is usually what you want, but do not forget about it. Sometimes ignored files might be precious, like cached data which takes hours to days to be calculated again.
$GIT_DIR/modules/<name>/?Probably people want to remove the cached repository, because they are afraid to run into a problem later. This is true, but running into that "problem" is the correct way to solve it! Because the fix is easy, and done right you will be able to live happily ever after. This avoids more cumbersome trouble than when you remove the data yourself.
Example:
mkdir tmptest &&
cd tmptest &&
git init &&
git submodule add https://github.com/hilbix/empty.git two &&
git commit -m . &&
git submodule deinit two &&
git rm two &&
git commit -m . &&
git submodule add https://github.com/hilbix/src.git two
The last line outputs following error:
A git directory for 'two' is found locally with remote(s):
origin https://github.com/hilbix/empty.git
If you want to reuse this local git directory instead of cloning again from
https://github.com/hilbix/src.git
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
Why this error? Because .git/modules/two/ previously was populated from https://github.com/hilbix/empty.git and now shall be re-populated from something else, namely https://github.com/hilbix/src.git. You won't see this if you re-populate it from https://github.com/hilbix/empty.git
What to do now? Well, just do exactly as told! Use --name someunusedname
git submodule add --name someunusedname https://github.com/hilbix/src.git two
.gitmodules then looks like
[submodule "someunusedname"]
path = two
url = https://github.com/hilbix/src.git
ls -1p .git/modules/ gives
someunusedname/
two/
This way in future you can switch branches/commit forward and backward and will never get into any trouble again, due to two/ having two different (and possibly incompatible) upstream repositories. And the best is: You keep both cached locally, too.
git).However if you removed the cached directory, both different checkouts will stumble upon each other, because you will not use the --name options, right? So each time you do the checkout you perhaps have to remove the .git/modules/<module>/ directory again and again. This is extremely cumbersome and makes it hard to use something like git bisect.
So there is a very technical reason to keep this module directory as a placeholder. People who recommend to remove something below .git/modules/ either do not know better or forget to tell you that this makes powerful features like git bisect nearly impossible to use if this crosses such a submodule incompatibility.
A further reason is shown above. Look at the ls. What do you see there?
Well, the 2nd variant of module two/ is not under .git/modules/two/, it is under .git/modules/someunusedname/! So things like git rm $module; rm -f .git/module/$module are totally wrong! You must either consult module/.git or .gitmodules to find the right thing to remove!
So not only most other answers fall into this dangerous trap, even very popular git extensions had this bug (it's now fixed there)! So better keep your hands of the .git/ directory if you do not exactly, what you are doing!
And from the philosophical view, wiping history is always wrong! Except for quantum mechanics, as usual, but this is something completely different.
FYI you probably guessed it: hilbix is my GitHub account.
All the answers look outdated. I am using git version 2.28.0. One line answer is,
git rm path-to-submodule
However, even though the submodule is removed from source control, .git/modules/path-to-submodule still contains the submodule repository and .git/config contains its URL, so you still have to remove those manually:
git config --remove-section submodule.path-to-submodule
rm -rf .git/modules/path-to-submodule
Sometimes, you have to use the -f flag:
$ git rm -f img2vec
For example, because you might get an error like this:
$ git rm img2vec/
error: the following file has changes staged in the index:
img2vec
(use --cached to keep the file, or -f to force removal)
With git v2.7.4 simple 3 steps worked just fine.
git submodule deinit -f -- a/submodule
git rm -f a/submodule
git commit
With git 2.17 and above it's just:
git submodule deinit -f {module_name}
git add {module_name}
git commit
$ git submodule deinit -f <submodule-name>
$ rm -rf .git/modules/<submodule-name>
$ git config -f .gitmodules --remove-section submodule.<submodule-name>
$ git config -f .git/config --remove-section submodule.<submodule-name>
$ git rm --cached <submodule-name>
$ git commit -m 'rm submodule: <submodule-name>'
To summarize, this is what you should do :
Set path_to_submodule var (no trailing slash):
path_to_submodule=path/to/submodule
Delete the relevant line from the .gitmodules file:
git config -f .gitmodules --remove-section submodule.$path_to_submodule
Delete the relevant section from .git/config
git config -f .git/config --remove-section submodule.$path_to_submodule
Unstage and remove $path_to_submodule only from the index (to prevent losing information)
git rm --cached $path_to_submodule
Track changes made to .gitmodules
git add .gitmodules
Commit the superproject
git commit -m "Remove submodule submodule_name"
Delete the now untracked submodule files
rm -rf $path_to_submodule
rm -rf .git/modules/$path_to_submodule
See also : Alternative guide lines
This worked for me. The above answers were showing this in the terminal and nothing else was happening
'fatal: not removing 'demolibapp' recursively without -r'
I've created a bash script to ease the removal process. It also checks whether there are changes in the repo left unsaved and asks for confirmation.
It has been tested on os x would be interesting to know if it works as is on common linux distros as well:
https://gist.github.com/fabifrank/cdc7e67fd194333760b060835ac0172f
In case you need to do it in one line command with bash script as below:
$ cd /path/to/your/repo && /bin/bash $HOME/remove_submodule.sh /path/to/the/submodule
Create bash script file in the $HOME dir named i.e. remove_submodule.sh:
#!/bin/bash
git config -f .gitmodules --remove-section submodule.$1
git config -f .git/config --remove-section submodule.$1
git rm --cached $1
git add .gitmodules
git commit -m "Remove submodule in $1"
rm -rf $1
rm -rf .git/modules/$1
git push origin $(git rev-parse --abbrev-ref HEAD) --force --quiet
If you use Magit under Emacs you can proceed as follows:
Go to your project root, then
M-x, magit-list-submodules
then
M-x, magit-submodule-remove
You will be asked which submodule you want to remove.
That's it!
(My Magit version is v3.3.0)
If you want to remove the submodule without deleting the folder from your local file system here is what worked for me:
MOD=example
git rm --cached -f apps/$MOD
git config -f .gitmodules --remove-section submodule.$MOD
In latest git just 4 operation is needed to remove the git submodule.
.gitmodulesgit add .gitmodulesgit rm --cached <path_to_submodule>git commit -m "Removed submodule xxx"git rm <submodule path> && git commit. This can be undone using git revert.
.gitmodules file. $GIT_DIR/modules/<name>/.Source: git help submodules
To remove a git submodule below 4 steps are needed.
.gitmodules file. Entry might be like mentioned below[submodule "path_to_submodule"]
path = path_to_submodule
url = url_path_to_submodule
git add .gitmodulesgit rm --cached <path_to_submodule>.git commit -m "Removed submodule xxx" and push.Additional 2 more steps mentioned below are needed to clean submodule completely in local cloned copy.
.git/config file. Entry might be like mentioned below[submodule "path_to_submodule"]
url = url_path_to_submodule
rm -rf .git/modules/path_to_submoduleThese 5th and 6th steps does not creates any changes which needs commit.