Git Submodules 介绍
- 1、为什么你值得读这篇文章?
- 2、为什么有 submodules?
- 3、了解 Git Submodules
- 3.1、如何让一个Git仓库变为另一个Git仓库的 submodule
- 3.2、submodule 的父子关系存在哪里
- 3.3、submodule 的父子关系信息怎么存
- 4、submodule 开发常用操作
- 4.1、添加子模块
- 4.2、实际开发操作
- 4.2.1、将远端包含有子模块的代码克隆下来
- 4.2.2、建分支操作
- 4.2.2、子模块开发合并到develop/test
- 4.2.2、子模块和父模块开发合并到master
- 5、总结
1、为什么你值得读这篇文章?
-
一些技术博客写的很详细,但不适合新人学习。
-
官方文档很全面,适合了解详细命令,但主次不分明。
本文会根据我的大量的 submodules 实践经验(包括工作和个人开发),只解释常用的命令。当你了解这些命令,你完全可以像我一样使用 Git Submodules。
2、为什么有 submodules?
-
解决公共代码问题。如果某些文件,在项目A和项目B中都会用到,例如组件库,那么这些文件可以作为 submodules 来管理,减少重复代码。(当然,该场景下npm包是另一解决方案,你需要选择一种方案。)
-
解决团队维护难题。如果一个大项目是一个大 Git 仓库,需要统一编译,不同的模块由不同团队维护,放在同一个 Git 仓库有诸多难处:例如多个团队的 MR 混在一起、权限难以区分等。这种情况即使公司内网 Git 权限做的足够精细,仓库管理员的学习成本也会很高,很难深度使用这种高级功能。为了解决多团队维护的难题,Git Submodules 也能大展身手,它可以让每个团队负责的模块就是一个 Git 仓库,这些 Git 仓库都被包含在同一个主 Git 项目下。(当然,微前端、微服务是另一种解决方案,你需要选择一种方案。)
3、了解 Git Submodules
有2个概念:主项目
、submodule(子模块)
。这两者各自都是完整的 Git 仓库。
3.1、如何让一个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 位于的文件夹路径)
3.2、submodule 的父子关系存在哪里
关系是保存在主项目的 Git 仓库中。
被当作 submodule 的 Git 仓库,其实不知道自己变成了 submodule,它更不知道爸爸们有谁。(意思是,当你打开某个被当作 submodule 的 Git 仓库首页时,或者拉下这个仓库时,没有任何痕迹表明它是个submodule。因为父子信息不存在这里,只存在爸爸那里。)
3.3、submodule 的父子关系信息怎么存
.gitmodules 文件
父子关系的信息保存在主项目的.gitmodules
文件,如果不是新加 submodule,这个文件通常不必改变了,因为信息比较固定。
这个文件中主要记录了子模块的url,如果添加的时候使用的ssh链接,那这个url就是ssh,如果是https链接,这个url就是https
submodule 的版本号
主项目还保存了对应 submodule 的版本号(commit id),没有冗余存储 submodule 的代码。
可以看到,这其实是个跳转到另一个仓库的链接,指明了具体的 commit id。
这个版本号,是需要经常变更的。
4、submodule 开发常用操作
4.1、添加子模块
在github中创建了三个项目,其中git-learn为主项目,git-learn-submodules1和git-learn-submodules2为子项目
将git-learn克隆到本地,然后执行git submodule add ...(仓库B的地址,即git clone时后面那串东西)
添加子模块到git-learn中
本地项目文件夹下面就多出来了两个文件夹(两个子项目)和一个文件(.gitmodules)
这是git上的目录,其中两个字模块用hash commit来维护,指向的是两个子仓库的地址
点击就跳转到对应子模块的目录:
到此,子模块初始化完成,接下来模拟实际工作场景
4.2、实际开发操作
4.2.1、将远端包含有子模块的代码克隆下来
git clone --recurse-submodules <主仓库url>
如:https://github.com/your-username/your-repository.git
4.2.2、建分支操作
下面以idea操作为例,使用idea打开git-learn文件夹
实际开发分为,develop、test、prod、master、feature、release和hotfix等,我们模拟简单建一下
从main新建一个prod分支,然后推送到远端,develop和test类似
建完后如下图:
4.2.2、子模块开发合并到develop/test
当我们要开发时,从最新的master新建一个feature分支,要注意的是,我们开发哪个模块,就只需要新建哪一个模块的feature就行,其他可以不用动
例如:我要开发git-learn-submodules1
模块,从master新建一个feature/0716-xxxx分支
其中,git-learn-submodules1
分支在feature/0716-xxxx分支,git-learn
和git-learn-submodules2
在master分支
现在对git-learn-submodules1
文件进行修改
提交修改,推送到远端开发分支
推送成功后,你的feature分支中就有了记录
现在,需要将你的代码,合到develop或者test分支,以develop为例
本地仓库切到develop分支,并且更新最新的develop代码(跟新最新代码,可以避免冲突):
merge最新的开发代码到develop环境中
成功以后develop会出现绿色小箭头,然后推送到远端仓库
这样你的开发环境中,就是最新的代码了,test同理
4.2.2、子模块和父模块开发合并到master
修改子模块和父模块
这个时候会出现下面情况
common-api.json
和sub1.txt
是我们修改的文件是因为git-learn-submodules1
子文件的hash不一致出现的问题(最新hash在develop,我们现在处与feature/0716-xxxx分支)
这种情况,我们先提交模块分支
然后推送到远端的feature/0716-xxxx分支
一般开发,没有合master权限,需要请求合并分支
在github (gitlab同理) 中子模块目录发起合并master请求
完成merge
这个时候,你的远端master上的git-learn-submodules1
是最新的,需要拉到本地仓库,这个时候本地master就是最新的了
然后提交公共模块
然后按照同样的方法去发起merge请求操作。
到此,合并完成
5、总结
画了一个流程图,加深印象
通过官方文档,你可以了解到更多场景,但是我从来没使用过其它场景了,因为用不到。本文描述的完全满足了我所有日常使用场景。
而高级场景会导致协作变困难,因为不是所有开发者都懂这些更复杂的命令和配置
英文文档:https://www.git-scm.com/book/en/v2/Git-Tools-Submodules
中文文档:https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97
GitHub地址:https://github.com/huang-hanson/git-learn