Git Subtree

time to read 8 min | 1525 words

Originally posted at 1/10/2011

I have no idea why, but yesterday I tried using the git-subtree project on a different machine, and it did not work. Today, I tried it on my main work machine, and It Just Worked.

At any rate, let us see where we are, shall we?

PS C:\Work\temp> git init R1
Initialized empty Git repository in C:/Work/temp/R1/.git/
PS C:\Work\temp> git init R2
Initialized empty Git repository in C:/Work/temp/R2/.git/
PS C:\Work\temp> git init Lic
Initialized empty Git repository in C:/Work/temp/Lic/.git/
PS C:\Work\temp> cd R1
PS C:\Work\temp\R1> echo "Hello Dolly" > Dolly.txt
PS C:\Work\temp\R1> git add --all
PS C:\Work\temp\R1> git commit -m "initial commit"
[master (root-commit) b507184] initial commit
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Dolly.txt
PS C:\Work\temp\R1> cd ..\R2
PS C:\Work\temp\R2> echo "Hello Jane" > Jane.txt
PS C:\Work\temp\R2> git add --all
PS C:\Work\temp\R2> git commit -m "initial commit"
[master (root-commit) ec99676] initial commit
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Jane.txt
PS C:\Work\temp\R2> cd ..\Lic
PS C:\Work\temp\Lic> echo "Copyright Ayende (C) 2011" > license.txt
PS C:\Work\temp\Lic> git add --all
PS C:\Work\temp\Lic> git commit -m "initial commit"
[master (root-commit) a3a9b48] initial commit
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 license.txt
PS C:\Work\temp\Lic> cd..
PS C:\Work\temp> git clone .\Lic Lic.Bare --bare
Cloning into bare repository Lic.Bare...
done.

Those are the current repositories, and we want to be able to share the Lic repository among the two projects. Note that we created a bare repository for Lic, because we can’t by default push to a remote repository if it is not bare.

Using git subtree, we can run:

PS C:\Work\temp> cd .\R1
PS C:\Work\temp\R1> git subtree add --prefix Legal C:\Work\temp\Lic.Bare master
git fetch C:\Work\temp\Lic.Bare master
warning: no common commits
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:\Work\temp\Lic.Bare
 * branch            master     -> FETCH_HEAD
Added dir 'Legal'
PS C:\Work\temp\R1> ls -recurse


    Directory: C:\Work\temp\R1


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         1/10/2011  11:59 AM            Legal
-a---         1/10/2011  11:58 AM         28 Dolly.txt


    Directory: C:\Work\temp\R1\Legal


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         1/10/2011  11:59 AM         56 license.txt

We do the same in the R2 repository:

PS C:\Work\temp\R1> cd ..\R2
PS C:\Work\temp\R2> git subtree add --prefix Legal C:\Work\temp\Lic.Bare master
git fetch C:\Work\temp\Lic.Bare master
warning: no common commits
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:\Work\temp\Lic.Bare
 * branch            master     -> FETCH_HEAD
Added dir 'Legal'

Now let us see what happen when we modify things…

PS C:\Work\temp\R2> echo "Not for Jihad use" > .\Legal\disclaimer.txt
PS C:\Work\temp\R2> git add --all
PS C:\Work\temp\R2> git commit -m "adding disclaimer"
[master 3ac3e15] adding disclaimer
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Legal/disclaimer.txt

Couple of things to note here:

  • We could add & commit from the root repository, because as far as Git is concerned, there is only one repository.
  • If we were to push our changes to the root repository location, it would include the changes just made.

This is a Good Thing, because if I want to create a branch / fork, I get everything, not just references.

Now, let us push our changes to the Lic repository:

PS C:\Work\temp\R2> git subtree push C:\Work\temp\Lic.Bare master --prefix Legal
git push using:  C:\Work\temp\Lic.Bare master
1/      4 (0)2/      4 (0)3/      4 (0)4/      4 (1)Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 320 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To C:\Work\temp\Lic.Bare
   a3a9b48..10fea68  10fea680b0783e0cf6e5d3ba5130d154557ffbe5 -> master

And now let us see how we get those changes back in the R1 repository:

PS C:\Work\temp\R1> git subtree pull C:\Work\temp\Lic.Bare master --prefix Legal
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:\Work\temp\Lic.Bare
 * branch            master     -> FETCH_HEAD
Merge made by recursive.
 Legal/disclaimer.txt |  Bin 0 -> 40 bytes
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Legal/disclaimer.txt
PS C:\Work\temp\R1> ls -recurse


    Directory: C:\Work\temp\R1


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         1/10/2011  12:04 PM            Legal
-a---         1/10/2011  11:58 AM         28 Dolly.txt


    Directory: C:\Work\temp\R1\Legal


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         1/10/2011  12:04 PM         40 disclaimer.txt
-a---         1/10/2011  11:59 AM         56 license.txt

There is another important advantage for git subtree, it is only me that have to use this, everyone else can just work with the usual git tools, and not have to be aware that I am sharing code between projects in this manner.