前言

Submodules (子模組) 其實是個很常見的概念,在專案中想要引入其他專案的代碼,亦或是第三方的函式庫,被引入的庫就可以被看為是一個子模組。

在 Node.js 的專案中,子模組是由 yarn / npm 來幫我們管理其中的依賴。其他的語言也有相對應的套件管理系統,像是 Ruby 有 RubyGems,Elixir 有 hex ……等,這些套件管理系統可以利用 semver 格式的版本號來拉取想要的子模組版本。

但如果想引入的專案並沒有放在套件管理系統中,或是這個語言根本沒有對應的套件管理系統的時候,要想管理子模組就開始會變得有點麻煩。我們需要有一個比較好的方式來靈活管理我們的子模組。

git submodule 指令剛好可以在這邊派上用場,它可以用來管理巢狀的 git 專案。你可以在專案中引用其他的專案,並以該專案的提交 Hash 當做所依賴的版本號 (有點像是用 git 當做你的套件管理系統XD ),但使用的步驟稍微有點複雜,下面會說明。

常用指令

新增子模組

想要把一個專案當做是子模組加到你想要的路徑之下,可以用

1
$ git submodule add <repository> [<path>]

舉個實際的例子

1
$ git submodule add https://github.com/kaddopur/hexo-theme-bubuzou.git themes/bubuzou

意思就是把 hexo-theme-bubuzou 這個專案加到 theme/bubuzou 這個路徑之下。你會發現有三個檔案被改動/新增了,其中兩個你可以看得到。

1
2
3
4
# .gitmodules
[submodule "themes/bubuzou"]
path = themes/bubuzou
url = git@github.com:kaddopur/hexo-theme-bubuzou.git

1
2
# themes/bubuzou
Subproject commit 51c576971e7f8f3693bd16ea21075e45758e7432

一個是 git 幫我們產生的 .gitmodules 設定檔,其中包含了儲存庫與路徑的對應關係。而另一個是 themes/bubuzou,由外層的 git 來看,它是一個子模組當前所使用的提交 Hash。至於那個看不到的改動是在 .git/config 當中,當你要刪除子模組時要記得去清掉。

我們可以將變動存為另一個提交,如此一來即完成了子模組的新增。

提交子模組更新

子模組有更動需要提交,請依照步驟如下

  1. 提交子模組更動
  2. 上傳到子模組的遠端儲存庫
  3. 提交上層專案更動
  4. 上傳到上層專案的遠端儲存庫

如果專案沒有放在遠端,則 2, 4 步可省略。若有的話,千萬記得一定要將子模組的提交上傳到遠端,不然就等著收共事者的信吧XD。

移除子模組

要移除子模組有點麻煩,一共 7 個步驟 (參考)

  1. 刪掉 .git/config 中相關的段落
  2. 刪掉 .gitmodules 中相關的段落
  3. 暫存 .gitmodules 的更動
  4. git rm --cached 停止追蹤子模組
  5. 刪掉 .git/modules/<submodule>
  6. 提交更新
  7. 真正把子模組刪除

實際的指令如下,每步一行

1
2
3
4
5
6
7
$ git submodule deinit themes/bubuzou
$ vi .gitmodules
$ git add .gitmodules
$ git rm --cached themes/bubuzou # 後面不帶 '/'
$ rm -rf .git/modules/themes/bubuzou
$ git commit -m 'remove bubuzou'
$ rm -rf themes/bubuzou

複製包含子模組的專案

直接 git clone 上層專案,你會發現子模組的資料夾存在但卻是空的。此時你要先運行

1
$ git submodule init

這個指令會將你在 .gitmodules 中的子模組設定寫入 .git/config 中,接下來再運行

1
$ git submodule update

這個時候子模組才會真正被下載下來

結論

如果你的專案常常被其他專案引用,這時就是你拆分專案成為一個子模組的好時機。但如果你是寫 Node.js 或是 Ruby,請儘量使用該語言的套件管理系統來公布你的專案以方便日後引用。真的沒有辦法了,再用 git submodule,因為它真的有點麻煩。

後記

這次會寫 git submodule 的文章是因為這個部落格用了一個新主題 bubuzou,而我想要對這個主題做一些修改並加入版本控制,所以我先 fork 這個主題後,將其以子模組的方式加入我的部落格中。而文內所提到的一些指令,主要是設定部落格主題時實際用到的,我想將之記錄下來以便日後查詢,或是有人要問的時候可以少費一些唇舌直接貼這篇。在此特別感謝強者我同事湯姆餡熱心教學。