目录
-
-
[1.1 痛点](#1.1 痛点 "#11-%E7%97%9B%E7%82%B9")
-
[1.2 解决思路](#1.2 解决思路 "#12-%E8%A7%A3%E5%86%B3%E6%80%9D%E8%B7%AF")
-
[1.3 什么是git子仓库](#1.3 什么是git子仓库 "#13-%E4%BB%80%E4%B9%88%E6%98%AFgit%E5%AD%90%E4%BB%93%E5%BA%93")
-
[1.4 git的两种子仓库方案](#1.4 git的两种子仓库方案 "#14-git%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AD%90%E4%BB%93%E5%BA%93%E6%96%B9%E6%A1%88")
-
[1.5 git subtree 对现有项目的影响](#1.5 git subtree 对现有项目的影响 "#15-git-subtree-%E5%AF%B9%E7%8E%B0%E6%9C%89%E9%A1%B9%E7%9B%AE%E7%9A%84%E5%BD%B1%E5%93%8D")
-
-
-
[2.1 创建子仓库](#2.1 创建子仓库 "#21-%E5%88%9B%E5%BB%BA%E5%AD%90%E4%BB%93%E5%BA%93")
-
[2.2 关联子仓库](#2.2 关联子仓库 "#22-%E5%85%B3%E8%81%94%E5%AD%90%E4%BB%93%E5%BA%93")
-
[2.3 拉取子仓库更新](#2.3 拉取子仓库更新 "#23-%E6%8B%89%E5%8F%96%E5%AD%90%E4%BB%93%E5%BA%93%E6%9B%B4%E6%96%B0")
-
[2.4 推送更改到子仓库](#2.4 推送更改到子仓库 "#24-%E6%8E%A8%E9%80%81%E6%9B%B4%E6%94%B9%E5%88%B0%E5%AD%90%E4%BB%93%E5%BA%93")
-
[2.5 细节](#2.5 细节 "#25-%E7%BB%86%E8%8A%82")
-
1背景
1.1 痛点
目前业务主要有A端和B端两个系统,这两个系统技术栈是完全相同的,许多功能也相同。所以在日常的开发过程中,产生了大量的重复工作,一个需求在A端完成后,还需要复制到B端,这样往往容易出现疏漏。
1.2 解决思路
实现代码复用目前,有下面两种方法:
-
抽象成NPM包进行复用
-
使用Git的子仓库对代码进行复用
由于本项目要实现业务代码复用,抽成 npm 包的方式就不太合适。
1.3 什么是git子仓库
通俗上的理解, 一个Git仓库下面放了多个其他的Git仓库,其他的Git仓库就是我们父级仓库的子仓库。
通过使用git子仓库将公共的组件抽离出来,实现在一端更改后,另一端通过git去合并代码,将我们从繁重的复制粘贴中解放出来。同时,可以在后续的需求中放入公共组件,通过增量的方式去应用这个技术,不会影响以前的代码。
1.4 git的两种子仓库方案
目前git实现子仓库有下面两种方案:
-
git submodule。 tdesign 使用的就是这种方案。
-
git subtree
两种方案的对比如下:
维度 | subtree | submodule | 优劣对比 |
---|---|---|---|
空间占用 | subtree 在初始化 add 时,会将子仓库 copy 到父仓库中,并产生至少一次 merge 记录。所以会占用大量父仓库空间 | submodule 在初始化 add 时,会在父仓库新建一个 .gitmodules 文件,用于保存子仓库的 commit hash 引用。所以不会占用父仓库空间 | submodule 更优 |
clone | subtree add 至父仓库之后,后续的 clone 操作与单一仓库操作相同 | 后续 clone 时 submodule 还需要 init/update 操作,且 submodule 子仓库有自己的分支。 流水线部署时需要更改配置。 | subtree 更优 |
update | 子仓库更新后,父仓库需要 subtree pull 操作,且命令行略长,需要指定 --prefix 参数。由于无法感知子仓库的存在,可能会产生 merge 冲突需要处理 | 子仓库更新后,父仓库需要 submodule update 操作。父仓库只需变动子仓库 hash 引用,不会出现冲突 | submodule 更优 |
commit | 父仓库直接提交父子仓库目录里的变动。若修改了子仓库的文件,则需要执行 subtree push | 父子仓库的变动需要单独分别提交。且注意先提交子仓库再提交父仓库 | subtree 更优 |
用一句话来描述 Git Subtree 的优势就是:
经由 Git Subtree 来维护的子项目代码,对于父项目来说是透明的,所有的开发人员看到的就是一个普通的目录,原来怎么做现在依旧那么做,只需要维护这个 Subtree 的人在合适的时候去做同步代码的操作。
1.5 git subtree 对现有项目的影响
使用git subtree 无需改变现有工程结构,可以只在新需求中使用它去复用代码,相当于它只是一个复制粘贴的工具。
2方案设计
2.1 创建子仓库
建立一个单独的git仓库命名为 common
, 可以创建如下的目录结构:
javascript
-common
-utils 公共的工具函数
-services 接口
-components 公共的组件
-hooks 公共的hooks
2.2 关联子仓库
然后在A端和B端添加common的远程仓库:
bash
git remote add common [common仓库地址]
建立父仓库和子仓库的依赖关系:
bash
git subtree add --prefix=src/common common master
将common远程仓库的master分支拷贝到父仓库的 src/common 目录下, 这时在两个项目的src目录多一个 common 的文件夹,我们可以像一个本地目录一样去使用里面的代码。
--prefix 可以用 -P 来代替,见下文。
2.3 拉取子仓库更新
bash
git subtree pull -P src/common common master
2.4 推送更改到子仓库
方法一 直接提交
bash
git subtree push -P src/common common master
subtree push实际上是遍历本工程每一次提交,把提交文件涉及到subtree目录的挑出来,同步到subtree工程,如果提交有很多,速度会非常慢。
方法二 拆分代码再push[推荐]
bash
git subtree split --rejoin -P src/common
git subtree push -P src/common common master
如果想要split成功,一定要去除 commit msg 的校验。
方法三 拆分代码到单独分支
bash
git subtree split --rejoin -P src/common -b split-common
git push common split-common
首先将 common 拆分到父仓库的 split-common 分支,可以通过 checkout 到这个分支查看内容。
2.5 删除子仓库
bash
git rm -r src/common
2.5 细节
在开发一个需求的时候, A端更改了 common 后,其他人只需要向以前一样在父仓库拉取代码。而当想在B端使用 common 代码,则需要将A端的代码同步到common 仓库,B拉取一下就行。
问题
git subtree split 无效
我们项目是基于 umi 脚手架开发的项目,这个脚手架自带了一个 gitHooks 会对 commit 的msg进行校验,而git subtree split 的原理就是通过 msg 进行判断。 解决方法:去掉 package.json 中的 commit 校验
json
{
"gitHooks": {
}
}
修改后没有同步
问题描述
修改一个后,没有push代码,慢慢导致后面两端的子仓库出现差异, 出现代码冲突。
解决方法
每次修改公共的代码都要 push 和 pull, 手动保持一致。
git subtree pull 冲突
错误信息如下
vbnet
fatal: refusing to merge unrelated histories
解决方法, 在 git subtree pull 时添加 --squash
参数, 类似于 git push 的 --allow-unrelated-historie
参数。
git subtree push 不上去
bash
git push using: common feature/20221214
cache for f1156335aca1314ff75ba328a850cbdd13affb5a already exists!
暂时无法解决
参考文章
# 为什么你的公司不应该使用git submodule # Git subtree用法与常见问题分析 # 用 Git Subtree 在多个 Git 项目间双向同步子项目 # Git subtree 要不要使用 --squash 参数 # 掌握Git的subtree[译]