Skip to content

Git Submodules 介绍

特点

  • 因为暴露的是源代码,引用方必须有子模块的读权限,所以适合团队内、组织内协作或个人开发。
  • 通常允许引用方开发者修改子模块代码,并提交。修改子模块代码的成本较低。
  • 开发者主要靠阅读源码了解API和机制。
  • 有版本管理机制,各个引用方可以按需更新。

适用场景

  • 团队、组织内部协作。
  • 引用方时常需要修改共享代码。

为什么有 submodules?

解决公共代码问题。如果某些文件,在项目A和项目B中都会用到,例如组件库,那么这些文件可以作为 submodules 来管理,减少重复代码。(当然,该场景下npm包是另一解决方案,你需要选择一种方案。) 解决团队维护难题。如果一个大项目是一个大 Git 仓库,需要统一编译,不同的模块由不同团队维护,放在同一个 Git 仓库有诸多难处:例如多个团队的 MR 混在一起、权限难以区分等。这种情况即使公司内网 Git 权限做的足够精细,仓库管理员的学习成本也会很高,很难深度使用这种高级功能。为了解决多团队维护的难题,Git Submodules 也能大展身手,它可以让每个团队负责的模块就是一个 Git 仓库,这些 Git 仓库都被包含在同一个主 Git 项目下。(当然,微前端、微服务是另一种解决方案,你需要选择一种方案。)

了解 Git Submodules

有2个概念:主项目、submodule(子模块)。这两者各自都是完整的 Git 仓库。

如何让一个Git仓库变为另一个Git仓库的 submodule

  • 创建Git仓库A。
  • 创建Git仓库B。
  • 在Git仓库A中,通过git submodule add ...(仓库B的地址,即git clone时后面那串东西),可以把仓库B当作仓库A的submodule,此时A就成了主项目。【注:B也可以做A的主项目,通过在仓库B执行git submodule add ...(A地址)即可,因为二者都是完整Git仓库,在建立父子关系前,没有差异的。】

注意事项

执行操作后,会在当前父项目下新建个文件夹,名字就是 submodule 仓库的名字。这个文件夹里面的内容,是 submodule 对应 Git 仓库的完整代码。 如果你希望换个名字,或者换个路径(例如放在某个更深的目录下),也是允许的,需要后面增加个路径参数,例如git submodule add ...(仓库地址) src/B(你希望 submodule 位于的文件夹路径)

submodule 的父子关系存在哪里

关系是保存在主项目的 Git 仓库中。 被当作 submodule 的 Git 仓库,其实不知道自己变成了 submodule,它更不知道爸爸们有谁。(意思是,当你打开某个被当作 submodule 的 Git 仓库首页时,或者拉下这个仓库时,没有任何痕迹表明它是个submodule。因为父子信息不存在这里,只存在爸爸那里。)

submodule 的父子关系信息怎么存

.gitmodules 文件 父子关系的信息保存在主项目的 .gitmodules 文件,如果不是新加 submodule,这个文件通常不必改变了,因为信息比较固定。 图片

submodule 的版本号

主项目还保存了对应 submodule 的版本号(commit id),没有冗余存储 submodule 的代码。

submodule 开发常用操作

如何给 submodule 仓库提交更新

方法一,像普通仓库一样更新

之前说过,submodule 仓库也是个普通的 Git 仓库,它并不知道自己有多少个爸爸。 我们可以直接git clone xxx这个仓库,像给普通 Git 仓库提交更新一样,去更新它。

方法二,在主项目中更新

例如主项目在文件夹A,A里面包含:

.git文件夹 READMD.md主项目的ReadMe文件。 B文件夹,是个 submodule。

我们可以进入B文件夹cd B,你会发现在B中,也可以执行git status等命令,此时的git命令都会是针对仓库B的,你可以在这里切换分支、提交更新,这时候,提交的都是submodule的变更。

注意事项

当你在文件夹B中做commit后,文件夹B里面就有了新的 commit id。此时主项目A中所记录的 submodule 的commit id也会更新。所以,你cd ..回到文件夹A后,会发现A有变更了,变更内容是:旧commid id变成了新的commit id。

图片 下面是git diff: 图片

如何在主项目仓库,拉取 submodule 的更新

方法一,cd submodule 后 git pull

在 submodule 中,所有git操作就当作一个普通的 Git 仓库就行,你可以切换分支、提交代码、拉取更新等。 这个方法,你可以拉取到 submodule 的master最新代码。但是如果这时候的commit id跟主项目里记录的 submodule 的 commit id 不一致,你会在主项目仓库看到diff,你可能需要提交主项目更新。

方法二,主项目执行git submodule update --remote [submodule文件夹相对路径]

这个方法会自动拉取submodule的主分支(通常叫master或main)的最新版本。效果跟方法一一致。 如果你不带参数[submodule文件夹相对路径],就会更新所有 submodules。

注意事项,更新后需提交主项目变更

当我们更新子项目后,相当于是把主项目记录的 submodule 的 commit id 给更新了,需要提交下主项目的变更。

方法三,主项目执行 git submodule update [submodule文件夹相对路径]

注意,这个方法会使 submodule 的分支处于主项目里指定的 commit id。可能并不是拉 submodule 的 master 最新代码。 所以,这种方法仅适用于,当主仓库里记录的 submodule 的 commit id 已经是最新的(可能被其他同事提交过)。或者你期望 submodule 跟主仓库记录的保持一致时,也可以使用该方法。

如何 clone 包含 submodule 的仓库

方法一,按需clone submodule

  1. 先git clone 主项目仓库并进入主项目文件夹,这时候submodule的文件夹都是空的。
  2. 执行git submodule init [submodule的文件夹的相对路径]。
  3. 执行git submodule update [submodule的文件夹的相对路径]。

这就按需clone了submodule。什么时候有用呢?跨团队协作某个主项目时,一些其它团队的submodule我们没必要安装,就不必执行init和update了。

合并第2、3步骤

第2、3步可以合并。使用以下命令:

js
git submodule update --init [submodule的文件夹的相对路径]

注意顺序,--init跟[submodule的文件夹的相对路径]的位置不可以调换噢。

图片

方法二,一次性clone所有 submodule

  1. 先git clone 主项目仓库,这时候submodule的文件夹都是空的。
  2. 执行git submodule init。
  3. 执行git submodule update。

只要不写submodule,那么就一次性检查该主项目的所有submodule,都拉下来。

图片

合并第2、3步骤

js
git submodule update --init

合并第1、2、3步骤

js
git clone --recurse-submodules [主项目Git仓库地址]

图片

如何创建 submodule 关系

cd到主项目,执行:

js
git submodule add ...(另一仓库地址,即git clone时后面那串东西)

下面可以指定 submodule 放到哪个子文件夹

js
git submodule add ...(另一仓库地址) [(可选,submodule下载的路径)]

Welcome to the site