Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Git submodule to subtree

How can I convert a git submodule (with a folder in the local file system as a remote) to a git subtree, preferably preserving the commit history of the submodule?

like image 769
ominug Avatar asked Jan 29 '15 12:01

ominug


People also ask

How do I create a subtree in git?

Adding a subtreeSpecify the prefix local directory into which you want to pull the subtree. Specify the remote repository URL [of the subtree being pulled in] Specify the remote branch [of the subtree being pulled in] Specify you want to squash all the remote repository's [the subtree's] logs.

Why you should not use git submodules?

This is because of some major drawbacks around git submodules, such as being locked to a specific version of the outer repo, the lacking of effective merge management, and the general notion that the Git repository itself doesn't really know it's now a multi-module repository.


1 Answers

The following bash script is based on Alexander Mikhailian's post (http://mikhailian.mova.org/node/233). I modified it slightly to call subtree add instead of read-tree. It will fetch the list of submodules from the .gitmodule and extract the module's prefix, name and url. It then removes each submodule and adds them back as a subtree at the same location. It also adds the remote of each submodule as a remote so you can update the subtree by providing its name instead of its url later on (i.e. git subtree pull -P Foo Foo master --squash instead of git subtree pull -P Foo https://example.com/foo.git master --squash)

You may remove the --squash argument if you want to import the full history of the subtree to your repository. With --squash, will only import the head of the subtree into your repository. This should probably be what most people want.

For more information, you may want to read this post by atlassian: http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

#!/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
like image 70
GaspardP Avatar answered Sep 21 '22 19:09

GaspardP