git submodule 的初体验

1,016 阅读6分钟

踏入新公司,发现了其管理项目的模式有两种形式:

  • monorepo:单仓库,多个项目之间可以相互引用代码。
  • git submodule:多仓库,一个项目引用其他项目的代码。

对于 monorepo 在前面学习有所了解,但是对于 git submodule 却是第一次接触,那么就来亲自体验一把。

git submodule 字面理解就是 git 的子模块。其作用就是,当你在一个项目中想使用另外一个项目的代码,且两个项目是独立的仓库,那么这时候你就该考虑使用它了。

首先,先创建两个项目,项目名分别为: gitsubmodule01gitsubmodule02,安装依赖,运行项目。

# 项目1
pnpm create vite gitsubmodule01 --template react
​
# 项目2
pnpm create vite gitsubmodule02 --template react

然后在 gitee 上创建两个仓库,用于分别存放两个项目。

04_1.png

最后把刚刚创建两个项目推上去,分别采用一些列指令:

git init 
git add .
git commit -m "init"
git remote add origin https://gitee.com/xxxx/gitsubmodule_01.git
git push -u origin "master"

当进行完上面的步骤之后,就可以顺利进行接下来的工作了。

gitsubmodule02 编写共享组件

gitsubmodule02 里面编写一个组件,其内容和运行效果如下:

04_2.png

非常的简单。

gitsubmodule01 使用共享组件

gitsubmodule01 项目想使用 gitsubmodule02 项目中编写的共享组件,那么我们就需要把 gitsubmodule02 项目作为子模块嵌入到 gitsubmodule01 里面。

如何嵌入呢?

# gitsubmodule01 项目中执行
git submodule add https://gitee.com/xxx/git-submodule02.git submodule02

就是把 gitsubmodule02 项目添加到 submoule02的目录下面,是这样的:

04_3.png

也会生成一个 .gitmodules 的文件,其内容为:

[submodule "submodule02"]
  path = submodule02  # 子模块路径
  url = https://gitee.com/xxxx/git-submodule02.git # git 仓库地址# 这里我还测试了一下,在深层目录中,执行该命令,生成的 path 属性也是深层次的
[submodule "src/layout/html_layout"]
  path = src/layout/html_layout
  url = https://gitee.com/xxxx/Three-Layout.git

这样就把 submodule02 项目中的代码嵌入到了 submodule01 项目中,然后被使用。

04_4.png

看,是不是非常的简单,so easy。。。只需要把项目嵌入,就可以使用其项目的代码。

体验完了,接下里就是了解一些 git submodule 的一些特性了。

理解单独的仓库

在前面过了,git submodule 是针对两个不同仓库的项目,也就是单独的仓库,在上面的初体验也是能够明显的感知的。那么针对 commit分支等,是不是也是单独的呢?

单独的提交

当分别对项目代码中进行修改时,例如:

04_5.png

使用 git status 查看文件状态时,发检测出两个文件发生了变动:

04_6.png

那么这时候有意思的就来了:

  • 当你站在 gitsubmodule01 目录上执行 git add . 命令时,只会提交当前项目的代码,而不会提交 submodule02 项目中的代码。
  • 当在 gitsubmodule01/submodule02 目录上执行 git add . 命令时,才会提交 submodule02 项目中的代码。

小结:

这里也就充分说明了,两个仓库是独立的,相互不影响。提交代码时,也就需要进入对应的文件夹中执行命令(利用工具就另说了哈)。

切换文件夹路径,也就是找路径中的 .git 文件。

细心观察: 当修改子模块一个文件的代码之后,会发现存在两个更新的日志文件。

04_11.png
  • 发现 submodule02 有更新文件(本来就应该存在的,因为内容发生了变化嘛)
  • 也会发现 submodule01 也有更新文件(diff 文件),那是更新什么内容呢?让我们一起看看里面的内容
04_12.png

因此,可以看出,当 submodule02 发生变化,submodule01 也会有更新文件,就是为了提醒我们,子模块有内容变动,父模块需要保持同步的,更新 commitId,保持 git 的同步。(大致意思)

单独的分支

在上面已经验证了每个项目都是单独的仓库,那么针对分支操作也是一样的。

gitsubmodule02 项目中存在两个分支:masterdev

那么就需要进入 submodule02 目录中,执行 git switch dev 命令就进行分支切换。

04_7.png

发现两个项目在不同的分支上。

在这里就会存在一个问题,就是分支可能会产生混乱。这里是演示项目,只存在两个分支,还没有什么强关联,迭代什么的。但是当分支多了,就会形成一种组合关系,那么管理项目就会产生一定的困难。

这里就需要谨慎开发,注意。

克隆仓库

克隆仓库很简单,执行 git clone 命令即可。那么针对含有子模块来说,有什么不同吗?

git clone https://gitee.com/xxx/gitsubmodule_01.git

克隆下来项目,安装依赖,运行项目。你就会发现报错,报的错误信息为:

04\_8.png

找不到文件,那么当手动去找文件的时候,也会发现 submodule02 就是一个标蓝的空文件夹

04\_9.png

submodule02 目录的文件不见了,那么这时候就需要执行命令来恢复:

    git submodule init # 初始化
    git submodule update # 更新,拉取代码# 或者
    git submodule update --init
    # 如果子模块里面含有子模块,可以直接采用递归解决
    git submodule update --init --recursive

.git 文件分析

先来看看 submodule02 目录里面的 .git 文件。

gitdir: ../.git/modules/submodule02

会发生,是去找上一级目录下的 .git 文件(也就是 gitsubmodule01 项目的 .git 文件),该文件夹下存在 modules 文件,里面就是存放着项目的所有子模块内容。

01_10.png

里面就包含了两个子模块,一个 submodule02,一个深层次目录测试的项目。

如果子模块 gitsubmodule02 项目中继续存在子模块,在其内部也会存在 modules 文件夹。

总结

  1. 子模块 .git 文件内容,具体在根项目中的 .git 文件中 modules 中保存
  2. 再次说明是单独的 git 仓库。

待续 git subtree

当一个项目想使用另外一个项目中的代码,并且每个项目独立仓库时,git submodule 是一个不错的选择。

git subtree 也是一个另外的选择,没有研究,后面待学。

后续补充: 犯错实践

可以不用看,因为我也不知道想说明什么,但是自己就是在这里踩坑了。

代码还原,当修改了子模块,就会出现两个更新的文件,一个在主模块(diff 文件),一个在子模块(更新文件)。如果存在一个种情况,就是不想要修改了,那么会这样:

04_13.png

如果子模块处于没有被 commit 时,还原主模块的 submodule02 文件是不成功的,因为子模块一直处于变动之中。

04_14.png 子模块 commit 了,那么就会出现一个新的 commitId, 那么主模块就会出现前面 commitId diff。 但是如果还原了主模块的 diff 文件,那么就会保持原来的 commitId (-Subproject commmit)。同时,分支的 head 也会回退到之前 commit 节点。

犯错误点: 千万不要在此基础上创建新的分支。虽然 head 一致,但是在切换的时候,总是会发现 diff 文件始终一直要修改(也可能是自己没有搞来)