Introduction
For those who familiar with CVS ampersand modules mechanism, git submodule is some how similar to it.
Prepare a submodule bare repository
Create 3 repositories that may use as submodules:
$ mkdir ~/project
$ cd ~/project
$ mkdir sub1.git sub2.git sub3.git
$ git init --bare sub1.git
Initialized empty Git repository in /tmp/test.sub/sub1.git/
$ git init --bare sub2.git
Initialized empty Git repository in /tmp/test.sub/sub2.git/
$ git init --bare sub3.git
Initialized empty Git repository in /tmp/test.sub/sub3.git/
Three bare repositories now exists in your home folder:
$ dir *.git
sub1.git:
HEAD branches config description hooks info objects refs
sub2.git:
HEAD branches config description hooks info objects refs
sub3.git:
HEAD branches config description hooks info objects refs
Prepare super git repository
The super git repository is an example of a git repository that hold some submodules.
$ mkdir super
$ cd ~/super
$ git init
$ echo “This is super” > readme
$ git add readme
$ git commit –m “Super commit”
Add submodules
Next, we add 3 sub modules into the super git repository:
$ git submodule add /tmp/test.sub/sub1.git
Initialized empty Git repository in /tmp/test.sub/super/sub1/.git/
warning: You appear to have cloned an empty repository.
fatal: You are on a branch yet to be born
Unable to checkout submodule 'sub1'
$ git submodule add /tmp/test.sub/sub2.git
Initialized empty Git repository in /tmp/test.sub/super/sub2/.git/
warning: You appear to have cloned an empty repository.
fatal: You are on a branch yet to be born
Unable to checkout submodule 'sub2'
$ git submodule add /tmp/test.sub/sub3.git
Initialized empty Git repository in /tmp/test.sub/super/sub3/.git/
warning: You appear to have cloned an empty repository.
fatal: You are on a branch yet to be born
Unable to checkout submodule 'sub3'
$ ls -a
. .. .git sub1 sub2 sub3
You may have noticed there are warning and error messages when execute “git submodule add” command. This is due to the sub modules are empty in the example. You won’t get the messages if submodule is not empty.
We should further re-execute “git submodule add” again to complete the task:
$ git submodule add /tmp/test.sub/sub1.git
Adding existing repo at 'sub1' to the index
$ git submodule add /tmp/test.sub/sub2.git
Adding existing repo at 'sub2' to the index
$ git submodule add /tmp/test.sub/sub3.git
Adding existing repo at 'sub3' to the index
$ ls -a
. .. .git .gitmodules sub1 sub2 sub3
A new file .gitmodules was created:
$ cat .gitmodules
[submodule "sub1"]
path = sub1
url = /home/user/sub1.git
[submodule "sub2"]
path = sub2
url = /home/user/sub2.git
[submodule "sub3"]
path = sub3
url = /home/user/sub3.git
We should now add some changes to all empty sub module directory:
$ cd sub1
$ touch README
$ git add README
$ git commit -m "commit #1"
[master (root-commit) 3a8d946] commit #1
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 201 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /tmp/test.sub/sub1.git
* [new branch] master -> master
$ cd ../sub2
$ touch README
$ git add README
$ git commit -m "commit #1"
[master (root-commit) 63b5d68] commit #1
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 200 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /tmp/test.sub/sub2.git
* [new branch] master -> master
$ cd ../sub3
$ touch README
$ git add README
$ git commit -m "commit #1"
[master (root-commit) 46b2a53] commit #1
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 201 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /tmp/test.sub/sub3.git
* [new branch] master –> master
$ cd ..
The git repository status now become
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: .gitmodules
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# sub1/
# sub2/
# sub3/
Add and commit submodule folder
$ git add sub1 sub2 sub3
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: .gitmodules
# new file: sub1
# new file: sub2
# new file: sub3
#
Commit changes:
$ git commit -m "super commit"
[master (root-commit) 7029640] super commit
4 files changed, 12 insertions(+), 0 deletions(-)
create mode 100644 .gitmodules
create mode 160000 sub1
create mode 160000 sub2
create mode 160000 sub3
$ git status
# On branch master
nothing to commit (working directory clean)
To further make a super git repository as bare repository, run
$ git clone –bare ~/super ~/super.git
$ git remote add origin ~/super.git
$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master
Clone a git repository with submodules
This example shows how to work with clone of a super git repository that contain submodules:
$ cd ~/project
$ git clone super.git super.clone
$ cd ~/super.clone
$ ls –a *
readme
sub1:
. ..
sub2:
. ..
sub3:
. ..
All sub module’s directory is empty at this moment. Run this to get updated commits
$ git submodule init$ git submodule update
Initialized empty Git repository in /home/user/super.clone2/sub1/.git/
Submodule path 'sub1': checked out '8acdbdd86119c4a9777bbefe13bd8f80c96a8b7a'
Initialized empty Git repository in /home/user/super.clone2/sub2/.git/
Submodule path 'sub2': checked out 'e390c3e3114fde7643a3544c0a262eff5a52e09c'
Initialized empty Git repository in /home/user/super.clone2/sub3/.git/
Submodule path 'sub3': checked out '001355362c4ed758f17206a3fbbb2f7637983434'
$ ls -a *
readme
sub1:
. .. .git readme
sub2:
. .. .git readme
sub3:
. .. .git readme
An alternate way to clone repository is run everything in a single command:
$ git clone --recursive ~/super.git ~/super.clone
Initialized empty Git repository in /tmp/submodules/super.clone2/.git/
Submodule 'sub1' (/home/user/sub1.git) registered for path 'sub1'
Submodule 'sub2' (/home/user/sub2.git) registered for path 'sub2'
Submodule 'sub3' (/home/user/sub3.git) registered for path 'sub3'
Initialized empty Git repository in /home/user/super.clone2/sub1/.git/
Submodule path 'sub1': checked out '8acdbdd86119c4a9777bbefe13bd8f80c96a8b7a'
Initialized empty Git repository in /home/user/super.clone2/sub2/.git/
Submodule path 'sub2': checked out 'e390c3e3114fde7643a3544c0a262eff5a52e09c'
Initialized empty Git repository in /home/user/super.clone2/sub3/.git/
Submodule path 'sub3': checked out '001355362c4ed758f17206a3fbbb2f7637983434'
$ ls -a *
sub1:
. .. .git README
sub2:
. .. .git README
sub3:
. .. .git README
Add changes to submodule
The following example shows a submodule workflow to add, commit, push new changes:
Check out a branch first
$ cd ~/project/super/sub1
super/sub1$ git branch
* (no branch)
master
super/sub1$ git checkout master
super/sub1$ git branch
* master
Make a changes
$ cat readme
This is submodule
super/sub1$ echo "New changes" >> README
super/sub1$ git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: readme
#
no changes added to commit (use "git add" and/or "git commit -a")
Commit and push changes
$ git commit -a -m "New changes"
[master 6a26854] New changes
1 files changed, 1 insertions(+), 0 deletions(-)
$ git push
Counting objects: 5, done.
Writing objects: 100% (3/3), 246 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /tmp/test.sub/sub1.git
3a8d946..6a26854 master –> master
Check status of submodule directory
$ cd ..
$ git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: sub1
#
no changes added to commit (use "git add" and/or "git commit -a")
The submodule “sub1” status is modified now.
Commit and push changes of submodule directory
$ git commit -a -m "commit sub1"
[master 6039eee] commit sub1
1 files changed, 1 insertions(+), 1 deletions(-)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
nothing to commit (working directory clean)
$ git push
Counting objects: 3, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 309 bytes, done.
Total 2 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
To /tmp/test.sub/super.git
7029640..6039eee master –> master
Update a submodule
Try this to pull changes from origin repository to update local repository:
Pull changes
$ cd ../super.clone
super.clone$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
Updating 7029640..6039eee
Fast-forward
sub1 | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
$ cat sub1/README
$ git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: sub1
#
no changes added to commit (use "git add" and/or "git commit -a")
Up to this stage, the file has not updated to latest HEAD version yet. After pull from origin, the status of sub1 show modified. Continue to next topic to complete submodule update task.
Update submodule
To pull latest commit snapshot from origin, run “git submodule update” to checkout latest committed snapshot:
$ git submodule update
Submodule path 'sub1': checked out '6a26854c7985d6ba516dcca8fc8cb42908bcb639'
$ git status
# On branch master
nothing to commit (working directory clean)
$ cat sub1/README
New Changes
The repository folder is in clean state now. sub1 folder also updated to latest committed snapshot. Let’s continue to check the status of sub1 by continue to next topic.
Detached HEAD in submodule
Let’s check the status of sub1 now:
$ cd sub1
$ cat README
New Changes
$ git branch
* (no branch)
master
The current position of sub1 point to a commit stage without any label. This is known as detached HEAD commit or headless commit. Commit detached HEAD changes is not encounrage. It may be problems for other collaborators to retrieve the changes.
Instead, we should checkout the master branch, work, commit changes and merge commits to master branch:
$ git checkout master
Previous HEAD position was 6a26854... New changes
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
$ cat README
$
The master branch’s README file is in last committed snapshot. Let’s check the status of super git repository after checkout:
$ cd ..
$ git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: sub1
#
no changes added to commit (use "git add" and/or "git commit -a")
It’s parent directory has changed to modified. That simply means sub1 doesn’t match with latest committed snapshot.
$ git merge origin
Updating 3a8d946..6a26854
Fast-forward
README | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
$ cat README
New Changes
You may also use “git pull” to replace the “git merge origin” . Let’s check parent status again:
$ cd ..
$ git status
# On branch master
nothing to commit (working directory clean)
It is in clean state now.
No comments:
Post a Comment