English ▾ 主題 ▾ 最新版本 ▾ gitsubmodules 最後更新於 2.52.0

名稱

gitsubmodules - 在另一個儲存庫中掛載儲存庫

概要

.gitmodules, $GIT_DIR/config
git submodule
git <command> --recurse-submodules

描述

子模組(submodule)是嵌入在另一個儲存庫中的儲存庫。子模組擁有自己的歷史記錄;而嵌入它的儲存庫則被稱為父專案(superproject)。

在檔案系統上,子模組通常(但非總是 - 請參見下方的「形式」)由以下部分組成:(i) 位於父專案 $GIT_DIR/modules/ 目錄下的 Git 目錄,(ii) 位於父專案工作目錄內的工作目錄,以及 (iii) 位於子模組工作目錄根目錄下,指向 (i) 的 .git 檔案。

假設子模組在 $GIT_DIR/modules/foo/ 有一個 Git 目錄,且在 path/to/bar/ 有一個工作目錄,父專案會透過樹狀結構中位於 path/to/bargitlink 項目,以及其 .gitmodules 檔案(參見 gitmodules[5])中格式為 submodule.foo.path = path/to/bar 的項目來追蹤該子模組。

gitlink 項目包含了父專案預期子模組工作目錄所在的提交物件名稱。

.gitmodules 檔案中的 submodule.foo.* 區段為 Git 的 porcelain 層提供了額外的提示。例如,submodule.foo.url 設定指定了從何處獲取該子模組。

子模組至少可用於兩種不同的使用場景:

  1. 使用另一個專案同時保持獨立的歷史記錄。子模組允許您將另一個專案的工作樹包含在您自己的工作樹中,同時保持兩個專案的歷史記錄分開。此外,由於子模組被固定在特定的版本,另一個專案可以在不影響父專案的情況下獨立開發,這使得父專案僅在需要時才將自身鎖定到新版本。

  2. 將(邏輯上單一的)專案拆分為多個儲存庫並將它們重新連結在一起。這可用於克服 Git 現有實作限制,以實現更細粒度的存取控制:

    • Git 儲存庫的大小:以目前的格式,Git 對於大型儲存庫的擴展性較差,特別是包含未透過樹狀結構間的增量運算壓縮的內容時。例如,您可以使用子模組來儲存大型二進位資產,並且這些儲存庫可以進行淺層複製(shallow clone),這樣您在本地就不會擁有龐大的歷史記錄。

    • 傳輸大小:以目前的格式,Git 要求必須存在整個工作樹。它不允許在 fetch 或 clone 時傳輸部分樹。如果您工作所在的專案由多個以子模組形式連結在父專案中的儲存庫組成,您可以避免獲取您不感興趣的儲存庫工作樹。

    • 存取控制:透過限制使用者對子模組的存取,可用於為不同使用者實作讀/寫策略。

子模組的設定

子模組的操作可以使用以下機制進行設定(按優先權從高到低排列):

  • 命令列:適用於那些支援將子模組作為路徑規格(pathspec)一部分的命令。大多數命令都有一個布林旗標 --recurse-submodules,指定是否遞迴進入子模組。例如 grepcheckout。有些命令接受列舉值(enum),例如 fetchpush,您可以在其中指定子模組如何受到影響。

  • 子模組內部的設定。這包括子模組中的 $GIT_DIR/config,但也包括樹中的設定,例如指定子模組內部命令行為的 .gitattributes.gitignore 檔案。

    例如,當您在父專案中執行 git status --ignore-submodules=none 時,會觀察到子模組 .gitignore 檔案的效果。它會透過在子模組中執行 status 來收集子模組工作目錄的資訊,同時注意子模組的 .gitignore 檔案。

    當在父專案中執行 git push --recurse-submodules=check 時,子模組的 $GIT_DIR/config 檔案會發揮作用,因為這會檢查子模組是否有任何未推送到遠端的變更。遠端儲存庫通常是在子模組的 $GIT_DIR/config 檔案中設定。

  • 父專案中的 $GIT_DIR/config 設定檔。Git 僅會遞迴進入活躍子模組(參見下方的「活躍子模組」章節)。

    如果子模組尚未初始化,則子模組內部的設定尚不存在,因此這裡設定了例如從何處獲取該子模組。

  • 父專案內的 .gitmodules 檔案。專案通常使用此檔案來建議上游儲存庫集合的預設值,以對應子模組名稱與其路徑之間的關係。

    此檔案主要用作父專案中子模組名稱與路徑之間的對應,以便定位子模組的 Git 目錄。

    如果子模組從未被初始化,這是唯一能找到子模組設定的地方。它作為最後的備用方案,指定從何處獲取該子模組。

形式

子模組可以採取以下形式:

  • 「說明」一節中描述的基本形式:具有 Git 目錄、工作目錄、gitlink 以及 .gitmodules 項目。

  • 「舊形式」子模組:一個帶有嵌入式 .git 目錄的工作目錄,以及父專案中的追蹤 gitlink.gitmodules 項目。這通常見於使用舊版 Git 生成的儲存庫。

    可以手動建構這些舊形式的儲存庫。

    當取消初始化或刪除(見下文)時,子模組的 Git 目錄會自動移動到父專案的 $GIT_DIR/modules/<name>/

  • 已取消初始化的子模組:具有 gitlink.gitmodules 項目,但沒有子模組工作目錄。子模組的 Git 目錄可能依然存在,因為取消初始化後 Git 目錄會被保留。本應是工作目錄的目錄則是空的。

    執行 git submodule deinit 可以取消初始化子模組。除了清空工作目錄外,該命令僅修改父專案的 $GIT_DIR/config 檔案,因此父專案的歷史記錄不受影響。這可以透過 git submodule init 復原。

  • 已刪除的子模組:執行 git rm <submodule-path> && git commit 可以刪除子模組。這可以透過 git revert 復原。

    刪除操作會移除父專案的追蹤資料,即 gitlink 項目和 .gitmodules 檔案中的區段。子模組的工作目錄會從檔案系統中移除,但 Git 目錄會被保留,以便在不需要從另一個儲存庫獲取的情況下,檢查過去的提交。

    要完全移除子模組,請手動刪除 $GIT_DIR/modules/<name>/

活躍子模組

若符合以下情況,子模組被視為活躍:

  1. submodule.<name>.active 設為 true

    或是

  2. 若子模組的路徑符合 submodule.active 中的路徑規格

    或是

  3. 若已設定 submodule.<name>.url

且這些項目會按此順序進行評估。

例如:

[submodule "foo"]
  active = false
  url = https://example.org/foo
[submodule "bar"]
  active = true
  url = https://example.org/bar
[submodule "baz"]
  url = https://example.org/baz

在上述設定中,只有子模組 barbaz 是活躍的,bar 是因為 (1),baz 是因為 (3)。foo 不活躍,因為 (1) 的優先權高於 (3)。

注意,(3) 是歷史遺留產物,如果 (1) 和 (2) 指定子模組不活躍,它將會被忽略。換句話說,如果我們將 submodule.<name>.active 設為 false,或者如果子模組的路徑被排除在 submodule.active 的路徑規格之外,那麼 url 是否存在並不重要。這在接下來的範例中有所說明。

[submodule "foo"]
  active = true
  url = https://example.org/foo
[submodule "bar"]
  url = https://example.org/bar
[submodule "baz"]
  url = https://example.org/baz
[submodule "bob"]
  ignore = true
[submodule]
  active = b*
  active = :(exclude) baz

在此,除了 baz 之外的所有子模組(foo, bar, bob)都是活躍的。foo 是因為它自己的 active 旗標,而其他所有子模組是因為子模組的 active 路徑規格,它指定了任何以 b 開頭但不包含 baz 的子模組也是活躍的,無論是否存在 .url 欄位。

第三方函式庫的工作流程

# Add a submodule
git submodule add <URL> <path>
# Occasionally update the submodule to a new version:
git -C <path> checkout <new-version>
git add <path>
git commit -m "update submodule to new version"
# See the list of submodules in a superproject
git submodule status
# See FORMS on removing submodules

人為拆分儲存庫的工作流程

# Enable recursion for relevant commands, such that
# regular commands recurse into submodules by default
git config --global submodule.recurse true
# Unlike most other commands below, clone still needs
# its own recurse flag:
git clone --recurse <URL> <directory>
cd <directory>
# Get to know the code:
git grep foo
git ls-files --recurse-submodules
注意
git ls-files 也需要它自己的 --recurse-submodules 旗標。
# Get new code
git fetch
git pull --rebase
# Change worktree
git checkout
git reset

實作細節

當 clone 或 pull 包含子模組的儲存庫時,子模組預設不會被簽出(checked out);您可以指示 clone 遞迴進入子模組。git submoduleinitupdate 子命令將保持子模組在您的工作樹中處於簽出狀態並位於適當的修訂版本。或者,您可以設定 submodule.recurse,讓 checkout 遞迴進入子模組(注意 submodule.recurse 也會影響其他 Git 命令,請參見 git-config[1] 以獲得完整清單)。

GIT

git[1] 套件的一部分