My notes i've taken while familiarizing git-submodule. Create a public and private directory and add 4 public submodules
$ rootdir=~/code/submod
$ pubdir=$rootdir/public
$ pridir=$roodir/private
$ mkdir -p $pubdir $pridir
Submodules are git repos under private directory, published in public directory git clone --bare
:
$ cd $pridir
$ for mod in a b c d; do
mkdir $mod
cd $mod
git init
echo "modl $mod" > $mod.txt
git add $mod.txt && git commit -m "first commit, module $mod"
git clone --bare . $pubdir/$mod.git
git remote add origin $pubdir/$mod.git
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
cd ..
done
The tree should look like this:
$ tree -d $rootdir
/Users/gosuri/code/gosuri/submod
├── private
│ ├── a
│ ├── b
│ ├── c
│ └── d
└── public
├── a.git
│ ├── hooks
│ ├── info
│ ├── objects
│ │ ├── 6f
│ │ ├── 8e
│ │ ├── c6
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
├── b.git
│ ├── hooks
│ ├── info
│ ├── objects
│ │ ├── 24
│ │ ├── 38
│ │ ├── 61
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
├── c.git
│ ├── hooks
│ ├── info
│ ├── objects
│ │ ├── 1a
│ │ ├── 4e
│ │ ├── f3
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
└── d.git
├── hooks
├── info
├── objects
│ ├── 0d
│ ├── 26
│ ├── 9f
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
54 directories
Create a public super project:
$ cd $pridir
$ mkdir super
$ cd super
$ git init
$ echo hi > super.txt
$ git add super.txt
$ git commit -am 'first commit, empty super proj'
$ git clone --bare . $pubdir/super.git
$ git remote add origin $pubdir/super.git
$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master
Add all the submodules to the super project in private dir. Paths should be absolute:
$ cd $pridir/super
$ for mod in a b c d; do git submodule add $pubdir/$mod.git $mod; done
$ ls -a
. .. .git .gitmodules a b c d super.txt
The "git submodule add" command does a couple of things:
- It clones the submodule under the current directory and by default checks out the master branch.
- It adds the submodule's clone path to the ".gitmodules" file and adds this file to the index, ready to be committed.
- It adds the submodule's current commit ID to the index, ready to be committed.
$ cat .gitmodules
[submodule "a"]
path = a
url = /Users/gosuri/code/gosuri/submod/public/a.git
[submodule "b"]
path = b
url = /Users/gosuri/code/gosuri/submod/public/b.git
...
$ git status
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: a
new file: b
new file: c
new file: d
Commit the super project and push:
$ git commit -m "Add submodules a, b, c, d."
$ git push
$ git submodule init
You're a 2nd dev, checkout the code to private2 directory
$ pridir2=$rootdir/private2
$ mkdir $pridir2
$ git clone $pubdir/super.git $pridir2/super
$ cd $pridir2/super
You'll notice the submodule directories are empty ls -a a
.
Updating is a two step process:
$ git submodule init
Submodule 'a' (/Users/gosuri/code/gosuri/submod/public/a.git) registered for path 'a'
...
$ git submodule update
Cloning into 'a'...
done.
Submodule path 'a': checked out 'dc62354547777fbd77644c3cad66faab78190db7'
...
The major difference between "submodule update" and "submodule add" is that "update" checks out a specific commit, rather than the tip of a branch. It's like checking out a tag: the head is detached, so you're not working on a branch.
$ cd a
$ git branch
* (HEAD detached at dc62354)
master
To make changes for submodule, checkout the branch, commmit and push.
$ cd $pridir2/super/a
$ git checkout master
$ echo "adding a line again" >> a.txt
$ git commit -a -m "Updated the submodule from within the superproject."
$ git push
$ cd ..
$ git add a
$ git commit -am 'updated submodule a'
$ git push
Switch back to the other private checkout; the new change should be visible
$ cd $pridir/super
$ git pull
$ git submodule update
$ cat a/a.txt
modl a
adding a line agai
$ git add submodule/
Using a /
tells git you're adding a directory and not a submodule. Git will think you want to delete the submodule and add files from submodule
dir instead.
Note: This could be gaurded with a pre-add hook
If you forget to publish the submodule change, others won't be able to clone the repository.
$ cd $pridir/a
$ echo i added another line to this file >> a.txt
$ git commit -a -m "doing it wrong this time"
$ cd ..
$ git add a
$ git commit -m "Updated submodule a again."
$ git push
$ cd $pridir2/super
$ git pull
$ git submodule update
fatal: reference is not a tree: b1ef6ea8b41c54c083a0b9ef8ba2d9d38b564eb5
Unable to checkout 'b1ef6ea8b41c54c083a0b9ef8ba2d9d38b564eb5' in submodule path 'a'
Note: This could be gaurded with a pre-push hook