Convert a git repository from a submodule to a subtree

This article was last updated on: February 7, 2024 pm

Three scripts

Alexander Mikhailian

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cat .gitmodules |while read i
do
if [[$i == \[submodule*]]; then
mpath=$(echo $i | cut -d\" -f2)
read i; read i;
murl=$(echo $i|cut -d\ -f3)
mcommit=`eval "git submodule status ${mpath} |cut -d\ -f2"`
mname=$(basename $mpath)
echo -e "$name\t$mpath\t$murl\t$mcommit"
git submodule deinit $mpath
git rm -r --cached $mpath
rm -rf $mpath
git remote add $mname $murl
git fetch $mname
git branch _$mname $mcommit
git read-tree --prefix=$mpath/ -u _$mname
fi
done
git rm .gitmodules

🐾Warning:

The following two scripts, write dead branch is master, if the main branch is not master, you need to make corresponding changes.

Nikita240 - Stack Overflow

📚️Reference:
I modified and improved it. The new subtree will now point to the same commit as the old submodule. Previously, the script simply downloaded the latest commit from the target repository, which could cause compatibility issues.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/bin/bash -x
# This script will convert all your git submodules into git subtrees.
# This script ensures that your new subtrees point to the same commits as the
# old submodules did, unlike most other scripts that do this.
# THIS SCRIPT MUST BE PLACED OUTSIDE OF YOUR REPOSITORY!!!!!!!!!!
# Otherwise, the script will interfere with the git commits.
# Save the script in your home directory as `~/subtrees.sh`
# `cd` into your repository
# Run `~/subtrees.sh`
# Enjoy!

# extract the list of submodules from .gitmodule
cat .gitmodules |while read i
do
if [[$i == \[submodule*]]; then
echo converting $i
read i
# extract the module's prefix
mpath=$(echo $i | grep -E "(\S+)$" -o)
echo path: $mpath
read i
# extract the url of the submodule
murl=$(echo $i|cut -d\= -f2|xargs)
echo url: $murl
# extract the module name
mname=$(basename $mpath)
echo name: $mname
# extract the referenced commit
mcommit=$(git submodule status $mpath | grep -E "\S+" -o | head -1)
echo commit: $mcommit
# deinit the module
git submodule deinit $mpath
# remove the module from git
git rm -r --cached $mpath
# remove the module from the filesystem
rm -rf $mpath
# commit the change
git commit -m "Removed $mpath submodule at commit $mcommit"
# add the remote
git remote add -f $mname $murl
# add the subtree
git subtree add --prefix $mpath $mcommit --squash
# commit any left over uncommited changes
git commit -a -m "$mname cleaned up"
# fetch the files
git fetch $murl master
echo
fi
done
git rm .gitmodules
git commit -a -m "Removed .gitmodules"

GaspardP - Stack Overflow

📚️Reference:

I modified it slightly, calledsubtree addInstead ofread-tree。 It will start with.gitmoduleto get a list of submodules, and extract the prefix, name, and URL of the module. It then removes each submodule and adds them as subtrees in the same location. It also adds each submodule’s remote as remote, so you can update the subtree by providing its name instead of its URL (iegit subtree pull -P Foo Foo master --squashInstead ofgit subtree pull -P Foo https://example.com/foo.git master --squash)。

If you want to import the entire history of the subtree into your repository, you can remove it --squash Parameter. use --squash, only the HEAD of the subtree will be imported into your repository. This is probably what most people want.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash -x
# extract the list of submodules from .gitmodule
cat .gitmodules |while read i
do
if [[$i == \[submodule*]]; then
echo converting $i

# extract the module's prefix
mpath=$(echo $i | cut -d\" -f2)

# skip two lines
read i; read i;

# extract the url of the submodule
murl=$(echo $i|cut -d\= -f2|xargs)

# extract the module name
mname=$(basename $mpath)

# deinit the module
git submodule deinit $mpath

# remove the module from git
git rm -r --cached $mpath

# remove the module from the filesystem
rm -rf $mpath

# commit the change
git commit -m "Removed $mpath submodule"

# add the remote
git remote add -f $mname $murl

# add the subtree
git subtree add --prefix $mpath $mname master --squash

# fetch the files
git fetch $murl master
fi
done
git rm .gitmodules

📚️ Reference documentation


Convert a git repository from a submodule to a subtree
https://e-whisper.com/posts/58551/
Author
east4ming
Posted on
April 27, 2022
Licensed under