问题背景
随着公司业务线的增加,不同的业务线不可避免的有些相似,甚至相同的逻辑,比如营销方略下有营销概览,数据中心下也有一个同样的营销概览模块,以后可能直投下也会添加一个类似的模块。它们的数据来源是相同的,交互也是大同小异。每个业务线都copy一遍代码,造成了大量的代码冗余,且后续修改也要挨个进行一遍,所以这显然不是一个好办法。
本文要介绍的git子模块,就是解决这个问题的一个方案,希望能够抛砖引玉。
git子模块定义
子模块允许你将一个Git仓库作为另一个Git仓库的子目录。
它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
子模块适用于主项目对子模块有依赖关系,却又并不关心子模块的内部开发的流程和细节;适用于整体复用的情况;
比如我上面描述的问题背景,就是典型的案例,营销方略主模块不关心营销概览子模块的实现,流程只要和数据中心保持一致即可;
下面以这个需求为例,分别简单介绍一下submodule和subtree的使用;
已知,子模块的项目地址是:https://xxx.com/ads-fe/marketing-overview-submodule.git
使用子模块的主项目是:https://xxx.com/ads-fe/jzt-strategy.git
git submodule
首先介绍一下submodule的使用。
添加子模块
进入主项目所在目录,添加子模块到指定目录下:
//语法: git submodule add [-b ] [-f|--force] [--name ] [--reference ] [--] []
git submodule add https://xxx.com/ads-fe/marketing-overview-submodule.git src/Overview
执行完添加后,页面会多出2个子目录,如图所示:
除了这2处外,.git目录下也有变化,用于记录子模块的变更记录:
协同开发的问题
当有协同人员开发人员同一项目时需要拉取代码:
git clone https://xxx.com/ads-fe/jzt-strategy.git
注意:此时目录是空的,需要执行以下代码:
git submodule init
git submodule update
子模块拉取最新代码
git submodule update --remote src/Overview
//更新多个子模块
git submodule foreach 'git pull origin master'
删除子模块
当需求变动或者添加错误的时候,需要删除子模块:
git submodule deinit -f src/Overview
上述命令删除的只是src/Overviews下的文件,此时,这些文件仍然存在,需要删除.git/module/src/Overview下的内容和修改.gitmodules
git subtree
使用subtree,同样可以实现在一个项目中引用其他项目的数据;但是和submodule方式不同的是,使用subtree外部的版本库会作为一个目录被整个复制到本版本库中,并且复制到本版本库中的子目录下的数据可以和原版本库数据建立跟踪关联。
添加子模块
使用下面的代码把子仓库的地址作为一个remote,方便记忆;
git remote add MarketingOverview https://xxx.com/ads-fe/marketing-overview-submodule.git
此时,git config文件会多出一条记录:
[remote "MarketingOverview"]
url = https://xxx.com/ads-fe/marketing-overview-submodule.git
fetch = +refs/heads/*:refs/remotes/MarketingOverview/*
拉取子模块代码
进入主项目所在目录,执行:
//语法:git subtree add --prefix=
git subtree add --prefix=src/Overview MarketingOverview master –squash
添加之后的src/Overview就是主项目jzt-strategy的一个普通文件夹,如果这时候jzt-strategy内容更新之后,正常git push即可,子项目对于主项目来说完全是透明的。
子模块拉取最新代码
git subtree pull --prefix=src/Overview MarketingOverview master --squash
删除子模块
删除subtree,直接删除子模块所在的目录即可,没有残留文件需要单独清理;
git rm -rf src/Overview/
补充
上面介绍了2种子模块的增加和删除功能,但是没有介绍修改后的操作,主要是我认为这里的使用场景是多业务线完整复用的场景,如果差异过多,需要自己单独的模块,就失去了复用的意义了。当然这并不代表修改功能不能使用,感兴趣的小伙伴可以去了解一下,这里就不赘述了。
submodule vs subtree
subtree优点:
- subtree相比submodule操作更简单;无论添加还是删除,都少了很多步骤;
- 不增加任何像.gitmodule这样的新的元数据文件;
- git subtree对于项目中的其他成员透明,意味着可以不知道git subtree的存在
submodule优点:
- git submodule在本地可以存在多个git代码仓库
- git subtree只有一个代码库,也就是说在项目内部依赖外部独立项目的时候,是完全无感知的操作。
子模块存在的问题
有些组件,方法或者常量定义,可能另一个子模块也在使用,
比如ajax请求,本着尽量少配置和少依赖的原则,子模块一般会有自己封装的ajax方法,因为此时不能默认主项目也一定有ajax请求,如果引用多个子模块,
每个模块都有自己的ajax方法,这就造成了一定程度的代码冗余;
考虑方案:
- 提取公共组件
- 常量及公共方法,可以单独发布一个工具包引入
- 常量及公共方法再作为一个子模块引入
总结
大家可以根据实际情况进行选择使用,如果只是依赖一个模块,subtree可能更简单一些,如果主项目依赖多个子项目,submodule才是最好的选择。
参考:https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97