Git 子庫使用

程式設計師的貓 發表於 2020-06-30
Git

軟體開發中有一個DRY(Don’t Repeat yourself)原則,或者說DIE(Duplication Is Evil)原則,指的是儘可能減少一切重複工作,重用一切可能重用的東西,小到提取重複性程式碼,大到重用一個模組。時間就是金錢,效率就是生命。
使用Git管理專案的時候,如果涉及到模組重用,比如你需要用到別的倉庫提供的功能,雖然你可以直接把別人倉庫(當然也可以是自己的倉庫)的程式碼複製到你的工程目錄下,但是這樣當別的倉庫更新了,我們也想相應的更新我們所依賴的程式碼,或者我們對依賴的程式碼做了些更改想要合併到原倉庫,這時候事情就比較麻煩了。所以,我們希望主模組和子模組的的管理是分開的,也就是主模組的更改、提交等,是不涉及子模組的,反過來子模組也一樣。但是又希望他們之間有一個依賴關係,怎麼辦呢?
Git為了這種困境提供了一個解決方案:子模組(submodule)。
為了便於理解,我們把當前工作工程倉庫成為主倉庫,主倉庫所依賴的子模組倉庫成為子倉庫。

建立倉庫

建立主倉庫

mkdir demo && cd demo && git init

建立子倉庫

建立子倉庫用到的命令是git submodule add <repo> [<path>]。其中repo是你要新增為子模組的倉庫URL,path是主工程下的一個路徑,就相當與你在主工程下建立的一個專門用於儲存子倉庫程式碼的資料夾,名字是任意的,只不過習慣上用third_party利於區分和理解。例如我們將pybind11新增為子模組,其相對路徑為third_party/pybind11,我們可以使用以下命令:

git submodule add https://github.com/pybind/pybind11 third_party/pybind11

值得注意的是,pybind11這個目錄在在使用命令前是不能存在與third_party目錄之下的。並且,此命令執行完成以後,子倉庫的程式碼就被拉取到third_party/pybind11之中了。

提交內容

主倉庫和子倉庫之間,除了子倉庫位於主倉庫內,主倉庫對子倉庫的程式碼有所依賴以外,他們之間是沒有其他任何聯絡的。他們呢就相當於兩個倉庫,主倉庫不會跟蹤子倉庫內容的變化,反過來子倉庫也不去跟蹤子倉庫的任何資訊。就類似古代周邊某些附屬小國,雖然名面上朝貢、附屬,但實際上小國的治理和天朝是分開的。

提交到主工程的倉庫

對主工程內容的提交操作,與一般無子工程的倉庫操作並無二致。

提交到子模組的倉庫

如果有需要對子模組的內容進行修改並提交到其倉庫,可以進入到子模組倉庫所在目錄,再進行相應的操作。例如,我們使用cd third_party/pybind11進入到子模組倉庫,這時候我們可以使用git add, git commit等對更改進行操作,這些操作之對子模組倉庫產生影響。

克隆帶子模組的倉庫到本地

當我們需要克隆一個帶子倉庫的倉庫時,我們希望子倉庫的內容也是跟主倉庫一起儲存在本地的。當使用git clone xxxx.git將主倉庫克隆到本地後,子倉庫所在目錄只有一個空目錄,子倉庫內容其實併為下載到本地。有兩種方法:

  1. 在克隆主倉庫時加上--recurse-submodules,使用這種方法,子倉庫內容也會同時下載下來,如果子倉庫還包含子倉庫,也會被同時下載下來。
git clone xxxx.git --recurse-submodules
  1. 第二種方法是使用下面兩條命令:
git submodule init
git submodule update

值得注意的是,不管使用以上那兩種方法,子倉庫的內容雖然下載下來了,但是此時子倉庫的狀態是處於一種HEAD detached的狀態,也就是此時,你對子倉庫的更改,就算你已經commit但是當你下次使用git submodule update你所作的更改也會丟失。所以,你學要checkout`到一個工作分支,例如:

git checkout master

更新子倉庫

更新子倉庫內容有兩種方法,一種是進入到子倉庫所在目錄,進行常規的拉取和合並操作;

cd ./third_party/pybind11 
git fecth 
git merge

另一種是使用git submodule update --remote

git submodule update --remote 
git submodule update --remote <submodule ame>

兩條命令的區別是,如果不帶子倉庫名,預設會更新所有子倉庫,如果只想更新其中某個,需要指定需要更新的子倉庫名。

總結

對於帶子倉庫的倉庫,有兩種情況:

  1. 本地新建倉庫,需要建立與遠端子倉庫的依賴關係;
  2. 克隆已有帶子倉庫的倉庫到本地。
    第一種情況:
git submodule add <repo> [<path>]

第二種情況:

git submodule init [<path>]
git submodule update
git checkout <branch>

至此,依賴關係建立起來了,兩個倉庫之間便可以獨自操作。
可以使用git add, git commit, git status等各自操作。一句話,說的底盤聽水的:在主倉庫目錄下,操作的是主倉庫內容;cd third_party/sumdir進入到子倉庫,操作的便是子倉庫內容。

References

  1. Git-Tools-Submodules
本作品採用《CC 協議》,轉載必須註明作者和本文連結

你還差得遠吶!