设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.53.0 无变更
-
2.52.0
2025-11-17
- 2.44.1 → 2.51.2 无更改
-
2.44.0
2024-02-23
- 2.43.1 → 2.43.7 无更改
-
2.43.0
2023-11-20
- 2.35.1 → 2.42.4 无更改
-
2.35.0
2022-01-24
- 2.29.1 → 2.34.8 无更改
-
2.29.0
2020-10-19
- 2.27.1 → 2.28.1 无变更
-
2.27.0
2020-06-01
- 2.25.1 → 2.26.3 无更改
-
2.25.0
2020-01-13
- 2.22.1 → 2.24.4 无更改
-
2.22.0
2019-06-07
- 2.19.3 → 2.21.4 无更改
-
2.19.2
2018-11-21
- 2.19.1 无更改
-
2.19.0
2018-09-10
- 2.17.0 → 2.18.5 无更改
-
2.16.6
2019-12-06
- 2.15.4 无更改
-
2.14.6
2019-12-06
描述
子模块是嵌入在另一个仓库中的仓库。子模块有自己的历史;它所嵌入的仓库被称为主项目(superproject)。
在文件系统上,子模块通常(但并非总是 - 见下文“形式”)由以下部分组成:(i) 位于主项目 $GIT_DIR/modules/ 目录下的 Git 目录,(ii) 位于主项目工作目录内的子模块工作目录,以及 (iii) 位于子模块工作目录根部的 .git 文件,该文件指向 (i)。
假设子模块的 Git 目录位于 $GIT_DIR/modules/foo/,工作目录位于 path/to/bar/,主项目通过 path/to/bar 处的树对象中的 gitlink 条目,以及其 .gitmodules 文件(见 gitmodules[5])中格式为 submodule.foo.path = path/to/bar 的条目来跟踪该子模块。
gitlink 条目包含主项目期望子模块工作目录所在的提交对象名称。
.gitmodules 文件中的 submodule.foo.* 节为 Git 的上层(porcelain)层级提供额外提示。例如,submodule.foo.url 设置指定了从何处获取子模块。
子模块至少可用于两种不同的用例
-
在维护独立历史的同时使用另一个项目。子模块允许你在自己的工作树中包含另一个项目的工作树,同时保持两个项目的历史独立。此外,由于子模块固定在任意版本,另一个项目可以独立开发而不影响主项目,允许主项目仅在需要时才固定到新版本。
-
将一个(逻辑上单一的)项目拆分为多个仓库并将其重新绑定在一起。这可以用来克服当前 Git 实现的限制,以获得更细粒度的访问控制
-
Git 仓库的大小:在当前形式下,对于包含无法通过树之间增量计算压缩的内容的大型仓库,Git 的扩展性较差。例如,你可以使用子模块来存放大型二进制资产,并且可以对这些仓库进行浅克隆(shallow clone),这样你就不会在本地保存庞大的历史记录。
-
传输大小:在当前形式下,Git 要求完整的工作树存在。它不允许在 fetch 或 clone 时传输部分树。如果你工作的项目由多个作为主项目子模块绑定的仓库组成,你可以避免获取你不感兴趣的仓库的工作树。
-
访问控制:通过限制用户对子模块的访问,这可以用来为不同用户实施读/写策略。
-
子模块的配置
子模块操作可以使用以下机制进行配置(按优先级从高到低排列):
-
支持将子模块作为路径规范(pathspecs)一部分的命令的命令行。大多数命令都有一个布尔标志
--recurse-submodules,用于指定是否递归进入子模块。例如grep和checkout。某些命令(如fetch和push)接受枚举值,你可以指定子模块受影响的方式。 -
子模块内部的配置。这包括子模块中的
$GIT_DIR/config,也包括树中的设置,如指定子模块内命令行为的.gitattributes或.gitignore文件。例如,当你在主项目中运行
gitstatus--ignore-submodules=none时,会观察到子模块.gitignore文件的效果。这通过在子模块中运行status并关注子模块的.gitignore文件来从子模块的工作目录收集信息。当在主项目中运行
gitpush--recurse-submodules=check时,子模块的$GIT_DIR/config文件会发挥作用,因为这会检查子模块是否有任何未发布到任何远程仓库的更改。远程仓库像往常一样在子模块的$GIT_DIR/config文件中配置。 -
主项目中的配置文件
$GIT_DIR/config。Git 仅递归进入活动(active)子模块(见下文“活动子模块”部分)。如果子模块尚未初始化,则子模块内部的配置尚不存在,因此例如从何处获取子模块就在此处配置。
-
主项目内部的
.gitmodules文件。项目通常使用此文件为上游仓库集合建议默认映射,该映射是子模块名称与其路径之间所必需的。此文件主要作为主项目中子模块名称和路径之间的映射,以便定位子模块的 Git 目录。
如果子模块从未被初始化,这是唯一能找到子模块配置的地方。它作为指定从何处获取子模块的最后退路。
形式
子模块可以采取以下形式:
-
“描述”部分中提到的基本形式,包含一个 Git 目录、一个工作目录、一个
gitlink和一个.gitmodules条目。 -
“旧式”子模块:一个带有嵌入式
.git目录的工作目录,以及主项目中的跟踪gitlink和.gitmodules条目。这常见于使用旧版本 Git 生成的仓库中。可以手动构建这些旧式仓库。
当取消初始化或删除(见下文)时,子模块的 Git 目录会自动移动到主项目的
$GIT_DIR/modules/<name>/。 -
取消初始化的子模块:有一个
gitlink和一个.gitmodules条目,但没有子模块工作目录。子模块的 Git 目录可能存在,因为在取消初始化后 Git 目录会被保留。本应作为工作目录的目录则是空的。可以通过运行
gitsubmoduledeinit来取消初始化子模块。除了清空工作目录外,该命令仅修改主项目的$GIT_DIR/config文件,因此不会影响主项目的历史记录。这可以使用gitsubmoduleinit来撤销。 -
已删除的子模块:可以通过运行 git rm <submodule-path> && git commit 来删除子模块。这可以使用
gitrevert来撤销。删除操作会移除主项目的跟踪数据,即
gitlink条目和.gitmodules文件中的相应小节。子模块的工作目录会从文件系统中移除,但 Git 目录会被保留,以便能够检出过去的提交而无需从另一个仓库获取。要完全移除一个子模块,请手动删除
$GIT_DIR/modules/<name>/。
活动子模块
子模块被视为活动的条件:
-
如果
submodule.<name>.active设置为true或
-
如果子模块的路径匹配
submodule.active中的路径规范或
-
如果设置了
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
在上述配置中,只有子模块 bar 和 baz 是活动的,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 是由于其自身的活动标志,其他所有子模块是由于子模块活动路径规范,该规范指定任何以 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 submodule 的 init 和 update 子命令将保持子模块已检出并在工作树中处于适当的修订版本。或者,你可以设置 submodule.recurse 以使 checkout 递归进入子模块(注意 submodule.recurse 也会影响其他 Git 命令,完整列表请参阅 git-config[1])。