Skip to content

Instantly share code, notes, and snippets.

@gosuri
Created December 17, 2015 02:25
Show Gist options
  • Save gosuri/239023b5d18bb4992b23 to your computer and use it in GitHub Desktop.
Save gosuri/239023b5d18bb4992b23 to your computer and use it in GitHub Desktop.
My notes i've taken while familiarizing git-submodule

git-submodule

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

Collaborating

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.

Getting changes (2 step)

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

Making changes

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

Gotachas

Never use a foward slash after the submodule name. Example:

$ 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

Publish submodule changes before the repo changes

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment