Git Tools - Submodules

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 pullgit 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

# 移除成功  

參考

http://www.kafeitu.me/git/2012/03/27/git-submodule.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岸梨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驰贷,更是在濱河造成了極大的恐慌盛嘿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件括袒,死亡現(xiàn)場離奇詭異次兆,居然都是意外死亡,警方通過查閱死者的電腦和手機锹锰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門芥炭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漓库,“玉大人,你說我怎么就攤上這事园蝠∶燧铮” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵彪薛,是天一觀的道長茂装。 經(jīng)常有香客問我,道長善延,這世上最難降的妖魔是什么少态? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮易遣,結(jié)果婚禮上彼妻,老公的妹妹穿的比我還像新娘。我一直安慰自己豆茫,他們只是感情好侨歉,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著揩魂,像睡著了一般幽邓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肤京,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天颊艳,我揣著相機與錄音,去河邊找鬼忘分。 笑死棋枕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妒峦。 我是一名探鬼主播重斑,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肯骇!你這毒婦竟也來了窥浪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤笛丙,失蹤者是張志新(化名)和其女友劉穎漾脂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胚鸯,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡骨稿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坦冠。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡形耗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辙浑,到底是詐尸還是另有隱情激涤,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布判呕,位于F島的核電站倦踢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏侠草。R本人自食惡果不足惜硼一,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梦抢。 院中可真熱鬧,春花似錦愧哟、人聲如沸奥吩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霞赫。三九已至,卻和暖如春肥矢,著一層夾襖步出監(jiān)牢的瞬間端衰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工甘改, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留旅东,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓十艾,卻偏偏與公主長得像抵代,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忘嫉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Git Tools - Submodules 1. 應(yīng)用場景 需求 當(dāng)你在一個項目 project1 上工作時荤牍,你...
    z嘉嘉嘉閱讀 1,323評論 0 1
  • 開發(fā)中我們經(jīng)常會遇到這樣的情況:項目越來越大,一些通用的模塊我們希望將他抽離出來作為單獨的項目庆冕,以便其他項目也可以...
    feil0n9wan9閱讀 20,020評論 0 24
  • 本文作者陳云峰康吵,轉(zhuǎn)載請注明。 這篇文章記錄個人常用的一些命令访递,和記不住的一些命令晦嵌,轉(zhuǎn)載了并不斷更新。 Git官網(wǎng) ...
    陳云峰閱讀 2,818評論 0 24
  • 如若不曾相遇 我們彼此守護(hù)著自己的牢房 不哭不棄 我們眺望著遠(yuǎn)景 不悲不傷 那怕秋葉落盡 哪怕容顏已老 如若不曾相...
    佑譯站閱讀 265評論 0 0
  • 瑪麗案例發(fā)生在1986年,羅杰斯去世之前的幾個月耍铜。 瑪麗案例是在一個“表現(xiàn)治療”的培訓(xùn)班上所做的示范面談邑闺。表現(xiàn)治療...
    暖陽_1332閱讀 1,286評論 0 0