简体中文 ▾ 主题 ▾ 最新版本 ▾ gitsubmodules 最后更新于 2.52.0

名称

gitsubmodules - 将一个仓库嵌入到另一个仓库中

概要

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

描述

子模块是在另一个仓库中嵌入的仓库。子模块有自己的历史;它被嵌入的仓库称为超项目。

在文件系统上,子模块通常(但不总是 - 请参阅下面的“形式”)包含(i)位于其超项目 $GIT_DIR/modules/ 目录下的 Git 目录、(ii)位于超项目工作目录内的子模块工作目录,以及位于子模块工作目录根目录下的 .git 文件,该文件指向(i)。

假设子模块在 $GIT_DIR/modules/foo/ 处有一个 Git 目录,在 path/to/bar/ 处有一个工作目录,超项目通过 gitlink 条目跟踪子模块,该条目位于 path/to/bar 的树中,并在其 .gitmodules 文件(请参阅 gitmodules[5])中有一个形式为 submodule.foo.path = path/to/bar 的条目。

gitlink 条目包含超项目期望子模块工作目录所处的提交的对象名称。

.gitmodules 文件中 submodule.foo.* 的部分为 Git 的客户端层提供了额外的提示。例如,submodule.foo.url 设置指定了子模块的获取位置。

子模块至少可以用于以下两种不同的用例:

  1. 在保持独立历史记录的同时使用另一个项目。子模块允许您在自己的工作目录中包含另一个项目的工作树,同时保持两个项目的历史记录分开。此外,由于子模块固定到任意版本,因此可以独立开发其他项目而不影响超项目,从而允许超项目仅在需要时固定到新版本。

  2. 将一个(逻辑上单一的)项目拆分成多个仓库并将它们重新关联起来。这可以用来克服 Git 实现当前的一些限制,以实现更细粒度的访问。

    • Git 仓库的大小:当前形式的 Git 在包含未通过树之间增量计算压缩的内容的大型仓库中的扩展性不佳。例如,您可以使用子模块来存储大型二进制资产,并且可以对这些仓库进行浅层克隆,这样您就不会在本地拥有庞大的历史记录。

    • 传输大小:当前形式的 Git 要求存在整个工作树。它不允许在 fetch 或 clone 时传输部分树。如果您正在处理的项目由多个仓库组成,这些仓库在超项目中作为子模块绑定在一起,您可以避免获取您不感兴趣的仓库的工作树。

    • 访问控制:通过限制用户对子模块的访问,可以为不同的用户实现读/写策略。

子模块的配置

子模块操作可以使用以下机制进行配置(从高到低优先级):

  • 支持将子模块作为其路径说明的一部分的命令的命令行。大多数命令都有一个布尔标志 --recurse-submodules,用于指定是否递归到子模块中。例如 grepcheckout。一些命令接受枚举值,例如 fetchpush,您可以在其中指定子模块受到的影响方式。

  • 子模块内部的配置。这包括子模块中的 $GIT_DIR/config,也包括树中的设置,例如 .gitattributes.gitignore 文件,这些文件指定了子模块内命令的行为。

    例如,当您在超项目中运行 git status --ignore-submodules=none 时,将观察到子模块 .gitignore 文件中的一个效果。这通过在子模块中运行 status 来收集子模块工作目录的信息,同时注意子模块的 .gitignore 文件。

    子模块的 $GIT_DIR/config 文件将在超项目中运行 git push --recurse-submodules=check 时发挥作用,因为这会检查子模块是否有任何未发布到任何远程的更改。远程像往常一样在子模块的 $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>.activefalse,或者子模块的路径在 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 标志,而其他所有子模块都是因为 submodule 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 递归到子模块中。git submoduleinitupdate 子命令将使子模块在您的工作树中保持检出状态并处于适当的修订版本。或者,您可以设置 submodule.recurse 以便 checkout 递归到子模块中(请注意,submodule.recurse 也会影响其他 Git 命令,有关完整列表,请参阅 git-config[1])。

GIT

Git[1] 套件的一部分