git submodule命令

git submodule命令用於初始化,更新或檢查子模塊。

使用語法

git submodule [--quiet] add [<options>] [--] <repository> [<path>]
git submodule [--quiet] status [--cached] [--recursive] [--] [<path>…​]
git submodule [--quiet] init [--] [<path>…​]
git submodule [--quiet] deinit [-f|--force] (--all|[--] <path>…​)
git submodule [--quiet] update [<options>] [--] [<path>…​]
git submodule [--quiet] summary [<options>] [--] [<path>…​]
git submodule [--quiet] foreach [--recursive] <command>
git submodule [--quiet] sync [--recursive] [--] [<path>…​]
git submodule [--quiet] absorbgitdirs [--] [<path>…​]

使用場景

基於公司的項目會越來越多,常常需要提取一個公共的類庫提供給多個項目使用,但是這個library怎麼和git在一起方便管理呢?

我們需要解決下面幾個問題:

  • 如何在git項目中導入library庫?
  • library庫在其他的項目中被修改了可以更新到遠程的代碼庫中?
  • 其他項目如何獲取到library庫最新的提交?
  • 如何在clone的時候能夠自動導入library庫?

解決以上問題,可以考慮使用git的 submodule 來解決。

submodule是什麼?

git submodule 是一個很好的多項目使用共同類庫的工具,它允許類庫項目做爲repository,子項目做爲一個單獨的git項目存在父項目中,子項目可以有自己的獨立的commitpushpull。而父項目以Submodule的形式包含子項目,父項目可以指定子項目header,父項目中會的提交信息包含Submodule的信息,再clone父項目的時候可以把Submodule初始化。

示例

以下是一些示例 -

1.添加子模塊

在本例中,我們將會添加一個名爲 「DbConnector」 的庫。

$ git submodule add http://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

默認情況下,子模塊會將子項目放到一個與倉庫同名的目錄中,本例中是 「DbConnector」。 如果你想要放到其他地方,那麼可以在命令結尾添加一個不同的路徑。

如果這時運行 git status,你會注意到幾個東西。

$ git status
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:   DbConnector

首先應當注意到新的 .gitmodules 文件。 該置文件保存了項目 URL 與已經拉取的本地目錄之間的映射:

$ cat .gitmodules
[submodule "DbConnector"]
    path = DbConnector
    url = http://github.com/chaconinc/DbConnector

如果有多個子模塊,該文件中就會有多條記錄。 要重點注意的是,該文件也像 .gitignore 文件一樣受到(通過)版本控制。 它會和該項目的其他部分一同被拉取推送。 這就是克隆該項目的人知道去哪獲得子模塊的原因。

git status 輸出中列出的另一個是項目文件夾記錄。 如果你運行 git diff,會看到類似下面的信息:

$ git diff --cached DbConnector
diff --git a/DbConnector b/DbConnector
new file mode 160000
index 0000000..c3f01dc
--- /dev/null
+++ b/DbConnector
@@ -0,0 +1 @@
+Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc

雖然 DbConnector 是工作目錄中的一個子目錄,但 Git 還是會將它視作一個子模塊。當你不在那個目錄中時,Git 並不會跟蹤它的內容, 而是將它看作該倉庫中的一個特殊提交。

如果你想看到更漂亮的差異輸出,可以給 git diff 傳遞 --submodule 選項。

$ git diff --cached --submodule
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..71fc376
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "DbConnector"]
+       path = DbConnector
+       url = http://github.com/chaconinc/DbConnector
Submodule DbConnector 0000000...c3f01dc (new submodule)

2.克隆含有子模塊的項目

接下來我們將會克隆一個含有子模塊的項目。 當你在克隆這樣的項目時,默認會包含該子模塊目錄,但其中還沒有任何文件:

$ git clone http://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
$ cd MainProject
$ ls -la
total 16
drwxr-xr-x   9 schacon  staff  306 Sep 17 15:21 .
drwxr-xr-x   7 schacon  staff  238 Sep 17 15:21 ..
drwxr-xr-x  13 schacon  staff  442 Sep 17 15:21 .git
-rw-r--r--   1 schacon  staff   92 Sep 17 15:21 .gitmodules
drwxr-xr-x   2 schacon  staff   68 Sep 17 15:21 DbConnector
-rw-r--r--   1 schacon  staff  756 Sep 17 15:21 Makefile
drwxr-xr-x   3 schacon  staff  102 Sep 17 15:21 includes
drwxr-xr-x   4 schacon  staff  136 Sep 17 15:21 scripts
drwxr-xr-x   4 schacon  staff  136 Sep 17 15:21 src
$ cd DbConnector/
$ ls
$

其中有 DbConnector 目錄,不過是空的。 你必須運行兩個命令:git submodule init 用來初始化本地配置文件,而 git submodule update 則從該項目中抓取所有數據並檢出父項目中列出的合適的提交。

$ git submodule init
Submodule 'DbConnector' (http://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
$ git submodule update
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

現在 DbConnector 子目錄是處在和之前提交時相同的狀態了。

不過還有更簡單一點的方式。 如果給 git clone 命令傳遞 --recursive 選項,它就會自動初始化並更新倉庫中的每一個子模塊。

$ git clone --recursive http://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (http://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

3. 刪除Submodule

git 並不支持直接刪除Submodule需要手動刪除對應的文件:

cd pod-project

git rm --cached pod-library
rm -rf pod-library
rm .gitmodules
更改git的配置文件config:
vim .git/config

可以看到Submodule的配置信息:

[submodule "pod-library"]
  url = [email protected]:jjz/pod-library.git

刪除submodule相關的內容,然後提交到遠程服務器:

git commit -a -m 'remove pod-library submodule'