网上关于git submodule的资料很多,但大部分都注重概念的介绍,看完之后,仍然一脸懵。正好最近项目中,有代码共享的场景,所以打算和大家分享下git submodule的实战落地过程。(PS:本文是站在前端开发的角度进行编写的)。
需求背景
最近遇到这样一个需求,在A、B两个系统中,其中有部分功能是相同的,例如某些路由页面,或者某些tab下对应的内容。之前这两个项目是独立开发的,但这就形成重复了劳动,所以从节约成本的角度出发,需要考虑使用某种方式来实现代码的共享。
方案探讨
对于上述场景,组内成员进行讨论后,主要有以下三种方案,
-
微前端 将公共的模块改造成子应用,原先的两个项目分别改造成主应用,然后再将公共的模块接入。 微前端技术现在已经比较成熟,并且部门内已经积极落地了。但是对于上述场景,公共的逻辑有些是处于单独的页面,有些处于其他页面的某Tab下,这样难免会出现同时存在几个子应用实例的场景,所以该方案不太可行。
-
npm包 将公共的代码做成一个npm包,然后在项目中引入就行。该方案的问题在于,需要重新搭建一个前端工程化项目,所以改造方案相对比较复杂。
-
git submodule 因为本质上,我们的需求就是代码共享,我们不希望再搭建一个工程化项目,所以
git submodule就是最好的选择。我们只需要把公共的逻辑抽离到子包中即可,主项目中引入子包后,就可以正常开发,子包完全可以看成主项目中一个普通的文件夹。
项目实战
我们创建了三个代码仓库,one、two、share。其中,one、two为主项目仓库,share为公共代码仓库。
添加子模块
我们首先通过git clone指令把主仓库代码都拉倒到本地,然后再通过git submodule add指令添加子模块。
git submodule add <仓库地址> <本地路径>
git submodule add http://xxx.com/share.git
不指定本地路径后,默认添加到主项目根目录下。
添加完子模块后,主仓库存在以下2处变动。
share文件夹就是子模块的代码。对于.gitmodules文件,我们来看下。
[submodule "share"]
path = share
url = http://xxx.com/share.git
里面存放着子模块的一些信息。我们公司的发布平台会检测项目中是否包含.gitsubmodules目录来判断是否使用了子模块。
子模块开发
在主项目根目录下,执行cd share进入子模块目录,命令行中输入git checkout -b feature/v1.0新建一个分支。子模块也是一个独立的git仓库,它的分支与主模块的分支是完全隔离的,互不影响。子模块中新增一个Base组件。
主模块中引入Base组件。
主项目启动后,可以发现,Base组件已成功引入。
代码提交
使用git submodule后,代码提交分为两步,分别为子模块代码提交,然后还需要在主项目中再进行一次代码提交。在子模块根目录进行提交代码后,我们可以发现,主项目中会同步子项目的commit哈希。
然后我们需要再主项目中再提交一次代码,否则远端主仓库无法同步此次子模块的修改。
子模块分支
在开发过程中,我们可以在子模块根目录下,可以手动切换子模块的分支,来同步最新的代码。但是项目部署时,在容器里面是无法手动来切换分支的,这个时候,我们需要怎么来处理呢?
更新子模块
我们重新从远端仓库clone一个one的代码。
我们会发现,主项目的代码已经拉取下来,但是展开share文件夹,空空如也。此时我们需要通过以下命令来同步子模块代码。
git submodule init
git submodule update
此时,子模块的代码也已经拉取完毕。但是我们会遇到一个问题,我们的子模块处于什么分支呢?回头梳理下,我们在子模块开发过程中,新建了一个feature/v1.0分支,并且进行了代码提交。进入子模块根目录,执行git branch -a来查看一下。
此时我们的子模块处于一个游离分支,通过哈希值对比可以发现,此时
HEAD指向上次的提交。
如果是处于开发阶段,我们可以通过git checkout feature/v1.0来切换到对应的分支。但容器部署时,改怎么办呢。
参考了一些网上的做法,具体实施如下,
我们可以在.gitmodules里面新增一个branch字段,具体如下:
[submodule "share"]
path = share
url = http://xxx.com/share.git
branch = feature/v1.0
然后可以通过Dockerfile或者其他方式,执行以下命令来自动切换分支。
sh -c "git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules
子模块删除
添加完子模块后,如果想删除子模块,步骤是比较复杂的,以share子模块为例,具体操作如下,切换到主项目根目录,
- 删除submodule缓存
git add .gitmodules
git rm --cached share
- 删除submodule目录
rm -rf share
- 修改.gitmodules
移除对应的子模块信息
- git/modules
移除该目录下对应子模块文件夹
- .git/config
删除该配置文件下对应子模块名对应的文件夹
总结
现在很多语言都有自己的包管理工具,例如js中有NPM,引入第三库来实现代码共享是最方便的,这也是git submodule使用不多的一个原因。但是在特定的场景下,尝试下git submodule却可以收到奇效。本文从实际项目需求出发,完成了整个流程,希望可以给大家带来一定的参考。