背景
公司要做一个服务平台,有12个领域,每个领域为一个微服务,现在前端项目暂时拆分为三个,分别为SEO,content和forward,三个项目(以后会拆分更多的项目)的组件、插件、工具、状态管理为同一套。现状复制三份一个项目放一份,这样的弊端是一个公共组件修改要同时维护三份,另外不同项目新增不同的组件,随着项目的增多组件的同步、维护成本会越来越大
需求分析
- (组件、插件、工具)、状态管理等公用代码迁移出去分别建立front-common和front-store(nuxt对vuex进行了封装,为了尽量不改动原目录结构)两个git 仓库,供其他项目共享代码
- 公用代码原本是什么样,抽取后也是什么样
- 公用代码库是可以在不同项目间双向同步的而不是单向同步
- 保留公用代码库的历史提交记录与双向同步记录
双向同步例子:A项目中依赖了子项目B,在A项目里改B子项目对应的目录里的代码,然后测试通过后,直接提交代码,这个更改也提交到B子项目的 Git仓库里。
同时子项目B也可以单独提交到 Git 仓库,在A项目里把子项目B的代码直接更新。
现有方案
- Git Submodule:这是Git官方以前的推荐方案
- Git Subtree:从 Git 1.5.2 开始,Git 新增并推荐使用这个功能来管理子项目
- npm:node package manager,实际上不仅仅是 node 的包管理工具
- composer:暂且认为他是php版npm
虽然 npm,composer,maven 等更侧重于包的依赖管理,以上几个方案都是能够做到在不同项目中同步同一块代码的,但没法双向同步,更适用于子项目代码比较稳定的情形。Git Submodule 和 Git Subtree 都是官方支持的功能,不具有依赖管理的功能,但能满足我们的要求。Git Subtree相对来说会更好一些 。
submodule 与 subtree对比
- git submodule
- 允许其他的仓库指定以一个commit嵌入仓库的子目录
- 仓库
clone
下来需要init
和update
- 会产
.gitmodule
文件记录 submodule 版本信息 - git submodule 删除起来比较费劲
- git subtree
- 避免以上问题
- 管理和更新流程比较方便
- git subtree合并子仓库到项目中的子目录。不用像submodule那样每次子项目修改了后要
init
和update
。万一哪次没update就直接add .
将.gitmodule
也commit
上去就悲剧了 - git v1.5.2以后建议使用git subtree
拆分已有项目
需要从现有项目中抽取公共模块单独进行git管理首先进行技术预演(展示网址为私有项目,自行替换自己的git仓库地址)
假设原项目test-sub-tree下web目录抽取项目store
- 进入项目目录:cd test-sub-tree
ps:直接使用git subtree add 的命令会出现 prefix ‘***’ already exists. 这样的错误提示,这是因为对应的目录已经存在,
不能直接添加,需要把对应的目录剥离开然后再加入subtree
2.剥离出store
git subtree split -P <store项目的相对路径> -b <临时branch>
Git 会遍历所有的commit,分离出store相关的commit,并存入front-store分支中
3. 创建子repo
在git远程创建front-store项目
退出项目目录,在原项目front同级目录创建文件夹,进入front-store目录,把这个目录变成Git可以管理的仓库:
cd …/
mkdir front-store
cd front-store
git init
拉取test-sub-tree项目front-store分支
git pull <test-sub-tree项目的路径> <临时branch>
如果直接用git clone <git地址>来拉取子项目并生成文件夹(我的common项目使用这种方法),执行上述命令
可能会报fatal: refusing to merge unrelated histories错误。
解决办法使用命令: git pull <test-sub-tree项目的路径> <临时branch> --allow-unrelated-histories
关联front-store远程仓库,推送代码到远程
git remote add origin <front-store项目的git仓库>
git push origin -u master
4. 清理数据
cd test-sub-tree项目的路径
git rm -rf <test-sub-tree项目的store相对路径>
git commit -m ‘移除store模块’ # 提交删除申请
git branch -D <临时branch> # 删除临时分支front-store
5. 添加subtree
git subtree add -P <store项目的相对路径> <store项目git地址> <分支> --squash
git push
解释:–squash意思是把subtree的改动合并成一次commit,这样就不用拉取子项目完整的历史记录。-P等于–prefix,之后的=等号可以用空格。
执行完第3步时,对应的目录已经剥离出来形成独立的项目了。第3,4步主要是把当前项目的对应的文件给删除,重新在test-sub-tree项目建立Subtree
项目间双向同步
test-sub-tree项目和front-store正常我们可以进行正常的日常git提交和修改维护
git add .
git commit -m “xxx文件修改”
git pull
git push
两个项目的代码要进行双向同步要执行下面的操作:
1. test-sub-tree项目修改store文件夹下的文件提交更改到front-store子项目
场景test-sub-tree项目删除store文件夹下supply_item文件
现在通过命令:
git subtree push -P <front-store项目的相对路径> <front-store项目git地址> <分支>
将“删除supply_item文件”这个操作同步到front-store子项目的仓库
进入front-store子项目文件夹,拉取远程仓库代码,会看到front-store文件夹下supply_item文件被删除
Git 会遍历步骤2中所有的commit,从中找出针对S目录的更改,然后把这些更改记录提交到S项目的Git服务器上,并保留步骤2中的相关S的提交记录到S仓库中
4. front-store子项目修改提交同步到test-sub-tree项目store文件夹下
场景front-store项目新增supply_item文件
现在通过命令:
git subtree pull --prefix=<front-store项目的相对路径> <front-store项目git地址> <分支> --squash
将front-store子项目的“新增supply_item文件”这个操作同步(拉取)到test-sub-tree项目
用git push将front-store子项目修改推送到test-sub-tree项目远程仓库
Tips:相对路径区别大小写