比如在公司不同開發團隊中,有一個基礎共享庫,同時被多個專案呼叫。若要保證基礎共享庫的動態更新,那麼就需要把共享庫獨立為一個程式碼庫,但是分別把共享庫中的檔案複製到各自的專案中會造成冗餘,而且再次更新共享庫就會在不同專案下更新,會比較麻煩。
利用子模組可以作為解決該類問題的一種方案,子模組允許將一個Git倉庫作為另一個Git倉庫的子目錄,並且可以獨立地管理這個子倉庫的版本,而且還能保持提交的獨立性。
下面就透過實操來具體談談 Git submodule
一、什麼是Git Submodule
Git Submodule
是Git版本控制系統中的一種機制,用於在一個Git倉庫中包含另一個Git倉庫。它允許將一個Git倉庫作為另一個Git倉庫的子目錄,並且可以獨立地管理這個子倉庫的版本,同時還保持提交的獨立。
Submodule的作用在於,它允許你在一個專案中使用其他專案的特定版本,而無需將整個子專案的程式碼複製到主專案中。這對於依賴管理和程式碼複用非常有用。
二、使用 Git Submodule
1 建立和初始化倉庫
- 初始化主庫和子模組庫
我採用的是 Gitee 來進行本次實操,首先在 Gitee 中建立一個主庫 GitSubmoduleDemo
,兩個子模組庫 ModuleA
和 ModuleB
:
先將主庫 clone 到本地:
$ git clone https://gitee.com/haohuin/git-submodule-demo.git
- 在主庫中,使用
git submodule add
命令新增Submodule
子模組。
其命令為:git submodule add <remote repo url> <local repo url>
- remote repo url 指遠端倉庫的 url
- local repo url 指本地倉庫的路徑 url
//在主庫中新增ModuleA
$ git submodule add ../module-a.git ModuleA
//在主庫中新增ModuleB
$ git submodule add ../module-b.git ModuleB
檢視當前主庫的狀態:
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: ModuleA
new file: ModuleB
發現有新的檔案在主庫中,分別是 .gitmodules
檔案,ModuleA
和 ModuleB
目錄
1.1 .gitmodules
檔案
首先看一下 .gitmodules
配置檔案,它儲存了遠端倉庫 url 與本地倉庫 url 路徑的對映:
[submodule "ModuleA"]
path = ModuleA
url = ../module-a.git
[submodule "ModuleB"]
path = ModuleB
url = ../module-b.git
每條記錄對應一個子模組資訊,path 和 url 對應遠端倉庫 url 和本地倉庫路徑
1.2 主庫中的子模組
接著提交當前主庫中出現的檔案和目錄:
//暫存
$ git add .
//提交
$ git commit -m "ModuleA and ModuleB submodule commit"
[master 2dcfefd] ModuleA and ModuleB submodule commit
3 files changed, 8 insertions(+)
create mode 100644 .gitmodules
create mode 160000 ModuleA
create mode 160000 ModuleB
發現ModuleA
和 ModuleB
目錄的模式編碼為 160000
。
在之前講解 index
檔案提到過,模式 160000
是引用其他 Git 倉庫的特殊檔案型別,它允許將一個 Git 倉庫作為另一個倉庫的子目錄進行管理。具體可以看這篇文章Git暫存區機制詳解
所以此時主庫中只是儲存了子模組的引用,也就是一次的提交記錄。而不是其他的檔案或者目錄。最後將主庫中的修改進行推送:
$ git push origin master
發現在主庫中ModuleA
和 ModuleB
確實是以引用的形式儲存的。
2 更新和同步子模組
那麼如何將子模組的修改更新到主倉庫中呢?這又分兩種情況:
- 在子模組所在的倉庫中修改程式碼後提交,然後在主倉庫中拉取更新
- 在主倉庫中修改子模組程式碼後提交遠端倉庫,然後在子模組倉庫中拉取更新
2.1 主倉庫更新子模組倉庫中的修改
首先在子模組倉庫 ModuleA 中新增一個檔案,並提交到 ModuleA 的遠端倉庫中:
$ echo "updateModuleA-1">updateModuleA-1.txt
$ git add .
$ git commit -m "update ModuleA"
$ git push
此時子模組倉庫中的 commit ID 值為 6026b48
,再來看一下主倉庫中的子模組的 Commit ID 值
發現此時主倉庫的 Commit ID 值為 3474554
,和子模組倉庫中的 ID 值並不同步,所以需要在主倉庫的子模組程式碼中同步子模組遠端倉庫的更新:
$ cd ModuleA
$ git checkout master
$ git pull origin master
然後在主倉庫根目錄中提交子模組倉庫推送和更新:
$ cd ..
$ git add .
$ git commit -m "update ModuleA"
$ git push
此時發現遠端主倉庫已經同步子模組的更新內容,而且 Commit ID 值也同步成6026b48
,和子模組倉庫中最新的 Commit ID 保持一致:
2.2 子模組倉庫更新主倉庫中子模組程式碼的修改提交
首先在主倉庫的子模組目錄下修改程式碼:
//在主倉庫中進入子模組ModuleA目錄
$ cd ModuleA
//修改檔案和提交和普通Git倉庫提交流程一致:
//1.建立檔案並提交
$ echo "ModuleA">updateModuleA.txt
$ git add .
$ git commit -m "update ModuleA"
//2.推送到遠端ModuleA倉庫
$ git push
發現遠端子模組倉庫也同步完成:
接著需要將主倉庫中的子模組 ModuleA 的變化同步到遠端主倉庫中具體的操作和上一節主倉庫更新子模組倉庫中的修改類似,用 add 和 commit 方式提交主倉庫的更新,這個時候就完成了子模組和主倉庫程式碼一致。
$ cd ..
$ git add .
$ git commit -m "update ModuleA"
$ git push
在實際的 IDE 環境中,只需要在子模組目錄下進行程式碼修改:
然後在提交時先更新子模組 ModuleB 的遠端倉庫:
然後再同步遠端主倉庫中的 ModuleB 目錄變化,這一步需要用命令列,直接在主目錄提交沒法更新遠端主目錄,但是本地主倉庫的 ModuleB 目錄確實發生了變化:
$ git status
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 restore <file>..." to discard changes in working directory)
modified: ModuleB (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
需要命令列切換到主倉庫目錄執行 git add
後再用 IDE 整合的 Git 提交操作:
$ git add .
再點選提交發現 IDE 中出現了 ModuleB 的更新變化:
接著推送到遠端主倉庫即可完成主倉庫和子模組倉庫的更新
三、總結
本文透過實踐介紹子模組:
- 包括子模組的初始化,如何使用 git submodule add 命令在主倉庫新增子模組
- 如何在子模組和主倉庫之間的更新和同步
子模組主要有以下應用場景
- 不同專案間需要共享同一個公共程式碼,如基礎類庫或工具包;
- 較大的專案需要拆分成多個子專案進行開發,透過子模組控制依賴關係;
- 第三方開源庫或元件的本地開發與專案進行繫結;
- 需要隔離成組和元件版本的同時利用依賴關係,如微服務架構下的服務與元件;
- 子專案有較強的獨立性同時也存在一定的耦合關係,透過子模組進行管理。
同時從上面的時間可以看到,子模組和主倉庫之間的同步比較複雜,維護成本高,子模組與主專案需要進行緊密的協調管理,否則很容易導致不同步的情況出現。在使用中要結合實際情況下操作,簡單的專案就不要用子模組,採用分支管理可能更合適。
參考資料
公共模組管理之 Git Submodule 使用總結
Git - 子模組
《Git 權威指南 蔣鑫著》