Git Tools - Submodules
1. 應(yīng)用場景
需求
當(dāng)你在一個項目 project1 上工作時严肪,你需要在其中使用另外一個項目 lib1 屎开。也許它是一個第三方開發(fā)的庫或者是你獨立開發(fā)和并在多個父項目 (priject2,prject2 ... ...)中使用的甫贯。這個場景下一個常見的問題產(chǎn)生了:你想將兩個項目 priject1吼鳞、lib1 單獨處理但是又需要在其中一個 project1 中使用另外一個 lib1。
解決方案
不使用 git submodules 的方法:包含庫
- 包含庫:把 lib1 代碼包含到 project1 (project2 叫搁、project3 ... ...)項目中
- 問題 : 定制和部署 project 更加困難了赔桌。
- 產(chǎn)生問題的原因 :因為你必須確保每個客戶都擁有 lib1 。并且當(dāng) lib1 被修改時(fix bug)渴逻,任何在 project1 (project2 疾党、project3 ... ...) 中對 lib1 的修改都很難歸并烂翰。
《Git權(quán)威指南》
項目的版本庫在某些情況下需要引用其他版本庫中的文件咙鞍,
例如公司積累了一套常用的函數(shù)庫佃牛,被多個項目調(diào)用盗似,顯然這個函數(shù)庫的代碼不能直接放到某個項目的代碼中垫言,而是要獨立為一個代碼庫祥款,那么其他項目要調(diào)用公共函數(shù)庫該如何處理呢炼列?
分別把公共函數(shù)庫的文件拷貝到各自的項目中會造成冗余圆恤,丟棄了公共函數(shù)庫的維護(hù)歷史聋袋,這顯然不是好的方法队伟。
Git Submodules
Git 通過子模塊處理這個問題。子模塊允許你將一個 Git 倉庫當(dāng)作另外一個Git倉庫的子目錄幽勒。這允許你克隆另外一個倉庫到你的項目中并且保持你的提交相對獨立嗜侮。學(xué)會 git submodule 可以使項目中再也不會出現(xiàn)大量
重復(fù)的資源文件、公共類庫啥容。更不會出現(xiàn)多個版本锈颗,甚至一個客戶多個項目風(fēng)格存在各種差異。
2. 使用 Git Submodules
本例子采用兩個項目以及兩個公共類庫學(xué)習(xí)對submodule的操作
2.1 創(chuàng)建 Git Submodule 測試項目
2.1.1 準(zhǔn)備環(huán)境
- 當(dāng)前目錄
? ~ pwd
/Users/yjizhang
? ~ mkdir -p submd/repos
- 創(chuàng)建需要的本地倉庫
? ~ cd ~/submd/repos
? repos git --git-dir=lib1.git init --bare
Initialized empty Git repository in /Users/yjizhang/submd/repos/lib1.git/
? repos git --git-dir=lib2.git init --bare
Initialized empty Git repository in /Users/yjizhang/submd/repos/lib2.git/
? repos git --git-dir=p1.git init --bare
Initialized empty Git repository in /Users/yjizhang/submd/repos/p1.git/
? repos git --git-dir=p2.git init --bare
Initialized empty Git repository in /Users/yjizhang/submd/repos/p2.git/
? repos ls
lib1.git lib2.git p1.git p2.git
- 初始化工作區(qū)
? ~ mkdir ~/submd/ws
? ~ cd submd/ws
2.1.2 初始化項目
- 初始化 p1 (project1)
? ~ cd ~/submd/ws
? ws git clone ../repos/p1.git
Cloning into 'p1'...
warning: You appear to have cloned an empty repository.
done.
? ws cd p1
? p1 git:(master) echo "p1" >> p1-infos.txt
? p1 git:(master) ? ls
p1-infos.txt
? p1 git:(master) ? gst
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
p1-infos.txt
nothing added to commit but untracked files present (use "git add" to track)
? p1 git:(master) ? git add p1-infos.txt
? p1 git:(master) ? gst
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: p1-infos.txt
? p1 git:(master) ? gc -m "init p1"
[master (root-commit) 960cb75] init p1
1 file changed, 1 insertion(+)
create mode 100644 p1-infos.txt
? p1 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 217 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
* [new branch] master -> master
? p1 git:(master)
- 初始化 p2
? ~ cd ~/submd/ws
? ws git clone ../repos/p2.git
Cloning into 'p2'...
warning: You appear to have cloned an empty repository.
done.
? ws cd p2
? p2 git:(master) gst
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
? p2 git:(master) echo "p2" >> p2-infos.txt
? p2 git:(master) ? ls
p2-infos.txt
? p2 git:(master) ? gst
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
p2-infos.txt
nothing added to commit but untracked files present (use "git add" to track)
? p2 git:(master) ? git add p2-infos.txt
? p2 git:(master) ? gst
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: p2-infos.txt
? p2 git:(master) ? gc -m "init p2"
[master (root-commit) 449e7ed] init p2
1 file changed, 1 insertion(+)
create mode 100644 p2-infos.txt
? p2 git:(master) gst
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
nothing to commit, working tree clean
? p2 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 217 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p2.git
* [new branch] master -> master
? p2 git:(master)
2.1.3 初始化公共類庫
- 初始化公共類庫 lib1
? ~ cd ~/submd/ws
? ws git clone ../repos/lib1.git
Cloning into 'lib1'...
warning: You appear to have cloned an empty repository.
done.
? ws cd lib1
? lib1 git:(master) echo "I'm lib1" > lib1-features
? lib1 git:(master) ? git add lib1-features
? lib1 git:(master) ? gc -m "init lib1"
[master (root-commit) 101b2e6] init lib1
1 file changed, 1 insertion(+)
create mode 100644 lib1-features
? lib1 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 224 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/lib1.git
* [new branch] master -> master
- 初始化公共類庫 lib2
? lib1 git:(master) cd ~/submd/ws
? ws git clone ../repos/lib2.git
Cloning into 'lib2'...
warning: You appear to have cloned an empty repository.
done.
? ws cd lib2
? lib2 git:(master) echo "I'm lib2" >> lib2-features
? lib2 git:(master) ? git add lib2-features
? lib2 git:(master) ? gc -m "init lib2"
[master (root-commit) fa8fdee] init lib2
1 file changed, 1 insertion(+)
create mode 100644 lib2-features
? lib2 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 225 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/lib2.git
* [new branch] master -> master
2.2 為主項目添加 Submodules
2.2.1 為 p1 添加 lib1 和 lib2
-
命令 :
git submodule add
? cd ~/submd/ws/p1
? p1 git:(master) ls
p1-infos.txt
? p1 git:(master) git submodule add ~/submd/repos/lib1.git libs/lib1
Cloning into '/Users/yjizhang/submd/ws/p1/libs/lib1'...
done.
? p1 git:(master) ? git submodule add ~/submd/repos/lib2.git libs/lib2
Cloning into '/Users/yjizhang/submd/ws/p1/libs/lib2'...
done.
? p1 git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: libs/lib1
new file: libs/lib2
? p1 git:(master) ? tree
.
├── libs
│ ├── lib1
│ │ └── lib1-features
│ └── lib2
│ └── lib2-features
└── p1-infos.txt
3 directories, 3 files
? p1 git:(master) ? cat libs/lib1/lib1-features
I'm lib1
? p1 git:(master) ? cat libs/lib2/lib2-features
I'm lib2
此時我們已經(jīng)成功使用 git submodule add
命令為 p1 添加了兩個公共類庫 lib1 lib2咪惠,那么我們怎樣查看子模塊是否已經(jīng)添加成功了呢 击吱?
2.2.2 查看 .gitmodules 的內(nèi)容
- .gitmodules : 該文件記錄了每個 submodule 的引用信息以及其在當(dāng)前項目的位置以及倉庫所在地址
? p1 git:(master) ? cat .gitmodules
[submodule "libs/lib1"]
path = libs/lib1
url = /Users/yjizhang/submd/repos/lib1.git
[submodule "libs/lib2"]
path = libs/lib2
url = /Users/yjizhang/submd/repos/lib2.git
2.2.3 提交添加子模塊的更改
? p1 git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: libs/lib1
new file: libs/lib2
? p1 git:(master) ? git commit -a -m "add submodule [lib1、lib2] to p1"
[master 65a41b4] add submodule [lib1遥昧、lib2] to p1
3 files changed, 8 insertions(+)
create mode 100644 .gitmodules
create mode 160000 libs/lib1
create mode 160000 libs/lib2
? p1 git:(master) git push
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 488 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
960cb75..65a41b4 master -> master
假設(shè)你是第一次引入公共類庫 lib1覆醇、lib2 到 p1 的開發(fā)人員,name項目組的其他人要怎么 Clone 帶有 Submodules 的項目呢 炭臭?
2.3 Clone 帶有 Submodule 的倉庫
模擬開發(fā)人員 B Clone 帶有 Submodule 的項目 p1
2.3.1 Clone 帶有 submodule 的庫 p1
? ~ cd ~/submd/ws
? ws git clone ../repos/p1.git p1-b
Cloning into 'p1-b'...
done.
? ws ls
lib1 lib2 p1 p1-b p2
# clone 成功永脓,查看 p1-b dir tree
? ws cd p1-b
? p1-b git:(master) tree
.
├── libs
│ ├── lib1
│ └── lib2
└── p1-infos.txt
# 對比 p1-b 和 p1 的 dir tree
? p1-b git:(master) cd ../p1
? p1 git:(master) tree
.
├── libs
│ ├── lib1
│ │ └── lib1-features
│ └── lib2
│ └── lib2-features
└── p1-infos.txt
3 directories, 3 files
發(fā)現(xiàn) 我們 clone 的 p1-b 中 submodule [lib1、lib2] 的內(nèi)容并沒有 clone 下來鞋仍。我們可以查看一下子模塊常摧,那么怎么查看子模塊狀態(tài)呢 ?
2.3.2 查看子模塊狀態(tài) git submodule
? p1-b git:(master) git submodule
-101b2e60e0f8c759d2f60c7fca12f3b1e474de21 libs/lib1
-fa8fdeed9d7c8938c0fe470766c568fb32b1ed14 libs/lib2
? p1-b git:(master) cd ../p1
? p1 git:(master) git submodule
101b2e60e0f8c759d2f60c7fca12f3b1e474de21 libs/lib1 (heads/master)
fa8fdeed9d7c8938c0fe470766c568fb32b1ed14 libs/lib2 (heads/master)
- 表示該子模塊還沒有檢出威创,怎么檢出子模塊內(nèi)容 ?
2.3.3 檢出子模塊內(nèi)容 git submodule init
& git submodule update
-
git submodule init
初始化本地配置文件.git/config
-
git submodule update
從指定項目.git/config
拉取所有數(shù)據(jù)并檢出你上層項目里所列的合適的提交
? p1-b git:(master) git submodule
-101b2e60e0f8c759d2f60c7fca12f3b1e474de21 libs/lib1
-fa8fdeed9d7c8938c0fe470766c568fb32b1ed14 libs/lib2
? p1-b git:(master) cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = /Users/yjizhang/submd/ws/../repos/p1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
? p1-b git:(master) git submodule init
Submodule 'libs/lib1' (/Users/yjizhang/submd/repos/lib1.git) registered for path 'libs/lib1'
Submodule 'libs/lib2' (/Users/yjizhang/submd/repos/lib2.git) registered for path 'libs/lib2'
? p1-b git:(master) cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = /Users/yjizhang/submd/ws/../repos/p1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[submodule "libs/lib1"]
url = /Users/yjizhang/submd/repos/lib1.git
[submodule "libs/lib2"]
url = /Users/yjizhang/submd/repos/lib2.git
? p1-b git:(master) git submodule update
Cloning into '/Users/yjizhang/submd/ws/p1-b/libs/lib1'...
done.
Cloning into '/Users/yjizhang/submd/ws/p1-b/libs/lib2'...
done.
Submodule path 'libs/lib1': checked out '101b2e60e0f8c759d2f60c7fca12f3b1e474de21'
Submodule path 'libs/lib2': checked out 'fa8fdeed9d7c8938c0fe470766c568fb32b1ed14'
? p1-b git:(master) git submodule
101b2e60e0f8c759d2f60c7fca12f3b1e474de21 libs/lib1 (heads/master)
fa8fdeed9d7c8938c0fe470766c568fb32b1ed14 libs/lib2 (heads/master)
? p1-b git:(master) tree
.
├── libs
│ ├── lib1
│ │ └── lib1-features
│ └── lib2
│ └── lib2-features
└── p1-infos.txt
3 directories, 3 files
? p1-b git:(master) cat libs/lib1/lib1-features libs/lib2/lib2-features
I'm lib1
I'm lib2
2.4 修改該有 Submodule 的庫
模擬開發(fā)人員 B 修改子模塊內(nèi)容
2.4.1 查看子模塊
? p1-b git:(master) cd libs/lib1
? lib1 git:(101b2e6) gst
HEAD detached at 101b2e6
nothing to commit, working tree clean
發(fā)現(xiàn)子模塊 lib1 在一個匿名的分支上落午,為什么 谎懦?
原因: Git 對于 Submodule 有特殊的處理方式。
-
一個主項目中引入 Submodule 其實 Git 做了三件事
- 記錄引用的倉庫 (子模塊倉庫描述)
- 記錄主項目中 Submodule 的目錄位置
- 記錄引用 Submodule 的 commit id
說明:
在 p1 中 push 之后其實是在 p1 中記錄了對 submodule 提交的引用 commit id
然后 p1-b 在 clone p1 的時候獲取到了 p1 對 submodule 提交的引用 commit id
當(dāng) p1-b 中執(zhí)行 git submodule update 的時候, git 根據(jù) modules 獲取到了 submodule 的 commit id , 然后獲取 submodule 的文件板甘。
所以 clone 之后子模塊不在任何分支上党瓮,但是 master 分支和 commit id 和 HEAD 保持一致。
2.4.2 更新子模塊
- 切換到 master 分支
- 更新子模塊內(nèi)容
- 提交子模塊更新
- 提交主項目中子模塊更新引用
# 切換到 master 分支
? p1-b git:(master) cd libs/lib1
? lib1 git:(101b2e6) gco master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
# 更新子模塊內(nèi)容
? lib1 git:(master) echo "add by developer b" >> lib1-features
# 提交子模塊更新
? lib1 git:(master) ? ga .
? lib1 git:(master) ? gc -m "update lib1-feature by dev b"
[master 241e03b] update lib1-feature by dev b
1 file changed, 1 insertion(+)
? lib1 git:(master) git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 286 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/repos/lib1.git
101b2e6..241e03b master -> master
# 提交主項目中子模塊更新引用
? lib1 git:(master) cd ../../
? p1-b git:(master) ? git diff
diff --git a/libs/lib1 b/libs/lib1
index 101b2e6..241e03b 160000
--- a/libs/lib1
+++ b/libs/lib1
@@ -1 +1 @@
-Subproject commit 101b2e60e0f8c759d2f60c7fca12f3b1e474de21
+Subproject commit 241e03bf0bf574063417c05a1d558fa4a6006425
(END)
? p1-b git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: libs/lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
? p1-b git:(master) ? git add libs/lib1
? p1-b git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: libs/lib1
? p1-b git:(master) ? gc -m "update libs/lib1 to lastest commit id"
[master ea785a2] update libs/lib1 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
? p1-b git:(master) git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 389 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
65a41b4..ea785a2 master -> master
現(xiàn)在我們已經(jīng)在 p1-b 中更新了子模塊 lib1 的內(nèi)容盐类。那么我們怎樣同步更新 p1 中 lib1 的內(nèi)容呢 ?
2.5 更新主項目的 Submodule
- 拉取主項目最新更新
git pull
或git pull -r (推薦.其會將本地提交放在遠(yuǎn)程提交的最后呛谜,減少沖突)
- 拉取子模塊更新的內(nèi)容
git submodule update
(注:在執(zhí)行前明確保已經(jīng)執(zhí)行了git submodule init
將子模塊信息注冊到.git/congfig
中)
? p1-b git:(master) cd ../p1
? p1 git:(master) gst
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
? p1 git:(master) git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/yjizhang/submd/ws/../repos/p1
65a41b4..ea785a2 master -> origin/master
Fetching submodule libs/lib1
From /Users/yjizhang/submd/repos/lib1
101b2e6..241e03b master -> origin/master
Updating 65a41b4..ea785a2
Fast-forward
libs/lib1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
? p1 git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: libs/lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
? p1 git:(master) ? git submodule update
Submodule path 'libs/lib1': checked out '241e03bf0bf574063417c05a1d558fa4a6006425'
? p1 git:(master) cat libs/lib1/lib1-features
I'm lib1
add by developer b
2.6 為 p2 添加子模塊 lib1 lib2
? ws cd ~/submd/ws/p2
? p2 git:(master) git submodule add ../../repos/lib1.git libs/lib1
Cloning into '/Users/yjizhang/submd/ws/p2/libs/lib1'...
done.
? p2 git:(master) ? git submodule add ../../repos/lib2.git libs/lib2
Cloning into '/Users/yjizhang/submd/ws/p2/libs/lib2'...
done.
? p2 git:(master) ? git add .
? p2 git:(master) ? gc -m "add submodules [lib1 lib2] to p2"
[master 742934c] add submodules [lib1 lib2] to p2
3 files changed, 8 insertions(+)
create mode 100644 .gitmodules
create mode 160000 libs/lib1
create mode 160000 libs/lib2
? p2 git:(master) git push origin master
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 467 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p2.git
449e7ed..742934c master -> master
? p2 git:(master)
2.7 修改 lib1 lib2 并同步到 p1 p2
- 使用場景: 假設(shè)開發(fā)人員 C 同時負(fù)責(zé) p1 和 p2 兩個項目在跳,當(dāng)他在開發(fā) p2 的時候發(fā)現(xiàn) lib1 / lib2 有 bug, 我們應(yīng)該怎么做 ?
-
示例需求如下:
- 在 lib1 中添加一個文件 README
- 在 lib2 中 lib2-features 中添加文字
learn git submodule
2.7.1 在 p2 的 lib1 中添加 README
? p2 git:(master) cd libs/lib1
? lib1 git:(master) touch README
? lib1 git:(master) ? ga .
? lib1 git:(master) ? gc -m "add readme to lib1"
[master 2c0e667] add readme to lib1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
? lib1 git:(master) git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 281 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/lib1.git
241e03b..2c0e667 master -> master
2.7.2 在 p2 的 lib2 中添加 learn git submodule
? lib1 git:(master) cd ../lib2
? lib2 git:(master) echo "learn git submodule" >> lib2-features
? lib2 git:(master) ? ga .
? lib2 git:(master) ? gc -m "add text to lib2 feature"
[master cc6dc70] add text to lib2 feature
1 file changed, 1 insertion(+)
? lib2 git:(master) git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 284 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/lib2.git
fa8fdee..cc6dc70 master -> master
2.7.3 更新 p2 中引用的 commit id
? p2 git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: libs/lib1 (new commits)
modified: libs/lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
? p2 git:(master) ? git add libs/lib1
? p2 git:(master) ? gc -m "update lib1 to lastest commit id"
[master 6be8d5f] update lib1 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
? p2 git:(master) ? git add libs/lib2
? p2 git:(master) ? gc -m "update lib2 to lastest commit id"
[master deebabf] update lib2 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
? p2 git:(master) git push
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 730 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p2.git
742934c..deebabf master -> master
2.7.4 同步 p2 中 lib1 lib2 的修改到 p1
? p1 git:(master) ? git pull
Already up-to-date.
? p1 git:(master) git submodule
241e03bf0bf574063417c05a1d558fa4a6006425 libs/lib1 (remotes/origin/HEAD)
fa8fdeed9d7c8938c0fe470766c568fb32b1ed14 libs/lib2 (heads/master)
# 對比 p2
? p2 git:(master) git submodule
2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93 libs/lib1 (heads/master)
cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7 libs/lib2 (heads/master)
# 兩個項目有兩個區(qū)別:
* commit id各不相同
* libs/lib1 所處的分支不同
# 可以發(fā)現(xiàn) p1 的 lib1 不在 master 分支上隐岛,所以我們要把 p1 lib1 切換到 master 分支上猫妙,拉取代碼
? p1 git:(master) ? cd libs/lib1
? lib1 git:(241e03b) gco master
Previous HEAD position was 241e03b... update lib1-feature by dev b
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
? lib1 git:(master) git pull
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 /Users/yjizhang/submd/repos/lib1
241e03b..2c0e667 master -> origin/master
Updating 101b2e6..2c0e667
Fast-forward
README | 0
lib1-features | 1 +
2 files changed, 1 insertion(+)
create mode 100644 README
# 拉取 lib2 的更新
? lib1 git:(master) cd ../lib2
? lib2 git:(master) git pull -r
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/yjizhang/submd/repos/lib2
fa8fdee..cc6dc70 master -> origin/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7.
# 查看 p1 當(dāng)前子模塊狀態(tài)
? lib2 git:(master) cd ../../
? p1 git:(master) ? git submodule
+2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93 libs/lib1 (heads/master)
+cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7 libs/lib2 (heads/master)
# 對比 p2
? p2 git:(master) git submodule
2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93 libs/lib1 (heads/master)
cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7 libs/lib2 (heads/master)
2.7.5 更新 p1 的 submodule 的引用
在上一步中我們更新了 p1 的 lib1 和 lib2 的最新版本,現(xiàn)在要把最新的commit id保存到 p1 中以保持最新的引用聚凹。
? p1 git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: libs/lib1 (new commits)
modified: libs/lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
? p1 git:(master) ? ga .
? p1 git:(master) ? gc -m "update lib1 lib2 commit id to new version"
[master 141bf28] update lib1 lib2 commit id to new version
2 files changed, 2 insertions(+), 2 deletions(-)
? p1 git:(master) git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 393 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
ea785a2..141bf28 master -> master
2.8 更新 p1-b 子模塊內(nèi)容
2.8.1 更新 p1-b 引用子模塊的 commit id
? p1-b git:(master) gst
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
? p1-b git:(master) git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/yjizhang/submd/ws/../repos/p1
ea785a2..141bf28 master -> origin/master
Fetching submodule libs/lib1
From /Users/yjizhang/submd/repos/lib1
241e03b..2c0e667 master -> origin/master
Fetching submodule libs/lib2
From /Users/yjizhang/submd/repos/lib2
fa8fdee..cc6dc70 master -> origin/master
Updating ea785a2..141bf28
Fast-forward
libs/lib1 | 2 +-
libs/lib2 | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
? p1-b git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: libs/lib1 (new commits)
modified: libs/lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
Git提示lib1和lib2有更新內(nèi)容割坠,這個判斷的依據(jù)來源于submodule commit id的引用。
那么現(xiàn)在怎么更新 p1-b 子模塊的內(nèi)容呢妒牙?
2.8.2 更新 p1-b 子模塊的內(nèi)容
現(xiàn)在我們只有兩個子模塊彼哼,我們固然可以像 p1 中那樣進(jìn)入子模塊的目錄然后 git checkout master
,接著 git pull
湘今,然后更新主庫引用的 commit id ; 但是敢朱,當(dāng)我們的項目中有很多個子模塊呢,我們一個一個地進(jìn)入子模塊的目錄中去更新子模塊內(nèi)容是不現(xiàn)實的摩瞎。解決這個問題的方式兩種:
git submodule foreach git pull
-
.sh
腳本
- 使用 git submodule foreach git pull 更新子模塊內(nèi)容
? p1-b git:(master) ? git submodule foreach git pull
Entering 'libs/lib1'
Updating 241e03b..2c0e667
Fast-forward
README | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
Entering 'libs/lib2'
You are not currently on a branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
Stopping at 'libs/lib2'; script returned non-zero status.
? p1-b git:(master) ? ls
libs p1-infos.txt
? p1-b git:(master) ? tree
.
├── libs
│ ├── lib1
│ │ ├── README
│ │ └── lib1-features
│ └── lib2
│ └── lib2-features
└── p1-infos.txt
3 directories, 4 files
- 使用
.sh
腳本更新子模塊內(nèi)容
? p1-b git:(master) ? mkdir bin
? p1-b git:(master) ? vim bin/update-submodule.sh
# 將下面的腳本復(fù)制到 update-submodule.sh 中
#!/bin/bash
grep path .gitmodules | awk '{ print $3 }' > /tmp/study-git-submodule-dirs
# read
while read LINE
do
echo $LINE
(cd ./$LINE && git checkout master && git pull)
done < /tmp/study-git-submodule-dirs
# 腳本執(zhí)行過程
* 首先把子模塊的路徑寫入到文件**/tmp/study-git-submodule-dirs**中拴签;
* 然后讀取文件中的子模塊路徑,依次切換到master分支(修改都是在master分支上進(jìn)行的)旗们,最后更新最近改動蚓哩。
# 給 .sh 腳本添加可執(zhí)行權(quán)限
? p1-b git:(master) ? cd bin
? bin git:(master) ? ll
total 8
-rw-r--r-- 1 yjizhang staff 219B Jan 7 20:11 update-submodule.sh
? bin git:(master) ? chmod +x update-submodule.sh
? bin git:(master) ? ll
total 8
-rwxr-xr-x 1 yjizhang staff 219B Jan 7 20:11 update-submodule.sh
? bin git:(master) ? cd ../
? p1-b git:(master) ? ./bin/update-submodule.sh
libs/lib1
Already on 'master'
Your branch is up-to-date with 'origin/master'.
Already up-to-date.
libs/lib2
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
Updating fa8fdee..cc6dc70
Fast-forward
lib2-features | 1 +
1 file changed, 1 insertion(+)
? p1-b git:(master) ? git submodule
2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93 libs/lib1 (heads/master)
cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7 libs/lib2 (heads/master)
# 更新后的 p1-b 子模塊 commit id 與 p2 的保持一致
# 添加 .sh 文件到 p1-b 庫中
? p1-b git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
bin/
nothing added to commit but untracked files present (use "git add" to track)
? p1-b git:(master) ? git add bin/update-submodule.sh
? p1-b git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: bin/update-submodule.sh
? p1-b git:(master) ? gc -m "add update submodules shell to p1"
[master beca8c0] add update submodules shell to p1
1 file changed, 9 insertions(+)
create mode 100755 bin/update-submodule.sh
? p1-b git:(master) git push
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 560 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
141bf28..beca8c0 master -> master
2.9 一次克隆包子模塊的庫
像 git submodule foreach git pull
一樣, git 提供了一次 clone 包含所有子模塊內(nèi)容的命令 git clone --recursive xx.git
? ws git clone --recursive ../repos/p1.git p1-submodules
Cloning into 'p1-submodules'...
done.
Submodule 'libs/lib1' (/Users/yjizhang/submd/repos/lib1.git) registered for path 'libs/lib1'
Submodule 'libs/lib2' (/Users/yjizhang/submd/repos/lib2.git) registered for path 'libs/lib2'
Cloning into '/Users/yjizhang/submd/ws/p1-submodules/libs/lib1'...
done.
Cloning into '/Users/yjizhang/submd/ws/p1-submodules/libs/lib2'...
done.
Submodule path 'libs/lib1': checked out '2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93'
Submodule path 'libs/lib2': checked out 'cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7'
? ws cd p1-submodules
? p1-submodules git:(master) tree
.
├── bin
│ └── update-submodule.sh
├── libs
│ ├── lib1
│ │ ├── README
│ │ └── lib1-features
│ └── lib2
│ └── lib2-features
└── p1-infos.txt
4 directories, 5 files
3.移除 Git Submodules
3.1 Clone 測試庫
從 p1 一個庫
? ws git clone --recursive ../repos/p1.git p1-remove-submodules
Cloning into 'p1-remove-submodules'...
done.
Submodule 'libs/lib1' (/Users/yjizhang/submd/repos/lib1.git) registered for path 'libs/lib1'
Submodule 'libs/lib2' (/Users/yjizhang/submd/repos/lib2.git) registered for path 'libs/lib2'
Cloning into '/Users/yjizhang/submd/ws/p1-remove-submodules/libs/lib1'...
done.
Cloning into '/Users/yjizhang/submd/ws/p1-remove-submodules/libs/lib2'...
done.
Submodule path 'libs/lib1': checked out '2c0e66770cc2b2f697cf0e86f3b51d737a5e6e93'
Submodule path 'libs/lib2': checked out 'cc6dc70a8a46965cd1b7860a859f9e4f2a0c34f7'
? ws cd p1-remove-submodules
3.2 移除子模塊
- 刪除git cache和物理文件夾
- 刪除.gitmodules的內(nèi)容(或者整個文件:如果要移除所有子模塊)
- 刪除.git/config的submodule配置
3.2.1 刪除git cache和物理文件夾
? p1-remove-submodules git:(master) git rm -r --cached libs/
rm 'libs/lib1'
rm 'libs/lib2'
? p1-remove-submodules git:(master) ? rm -rf libs
? p1-remove-submodules git:(master) ? ls
bin p1-infos.txt
3.2.2 刪除.gitmodules的內(nèi)容(或者整個文件:如果要移除所有子模塊)
? p1-remove-submodules git:(master) ? rm -rf .gitmodules
# 將 p1-remove-submodules 所有的子模塊都刪除了
# 如果要移除其中一個子模塊上渴,那么打開 .gitmodules 刪除該文件中需要移除的子模塊配置即可
3.2.3 刪除.git/config的submodule配置
? p1-remove-submodules git:(master) ? cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = /Users/yjizhang/submd/ws/../repos/p1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[submodule "libs/lib1"]
url = /Users/yjizhang/submd/repos/lib1.git
[submodule "libs/lib2"]
url = /Users/yjizhang/submd/repos/lib2.git
# 刪除 lib1 lib2 的配置
? p1-remove-submodules git:(master) ? cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = /Users/yjizhang/submd/ws/../repos/p1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
? p1-remove-submodules git:(master) ? gst
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: libs/lib1
deleted: libs/lib2
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: .gitmodules
? p1-remove-submodules git:(master) ? git add .
? p1-remove-submodules git:(master) ? gc -m "delete submodule lib1 and lib2"
[master 28e23f9] delete submodule lib1 and lib2
3 files changed, 8 deletions(-)
delete mode 100644 .gitmodules
delete mode 160000 libs/lib1
delete mode 160000 libs/lib2
? p1-remove-submodules git:(master) git push
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 279 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To /Users/yjizhang/submd/ws/../repos/p1.git
beca8c0..28e23f9 master -> master
# 移除成功