git subtree使用说明
为什么要使用subtree
在实际的项目开发过程中,公共的代码或者模块是必定会出现的,为了不重复写相同的代码;普遍的做法就是将其抽取成一个公共模块,这个模块由不同的使用者引用;作为java工程师,可能会选择将这一部分打包封装成一个jar,并且将其推送到Maven的私有仓库,各个使用者将其添加到pom文件即可;但是有没有更好的方式呢?因为使用这种方式,对使用者来说,并不是透明的,当前可能已经更新版本了,但是我作为使用者并不知道当前已经更新了,知道更新了可能也不知道最新版本是多少,我还得通过咨询模块开发人员或者说到私有仓库去查看才能知道,那有没有更好的方式呢?
接下来,就一起探讨一种更好的管理方式:git subtree
,如果说你现在还不知道git是啥或者怎么用?强力建议去学习一下。
什么是git subtree?有啥优势?
- git subtree可以将一个仓库作为仓库的子仓库
- 各个仓库之前的版本管理是相对独立的
- 对于使用者来说,是透明的,可能使用者根本都不知道有子仓库的存在;
- 主子仓库的分支同步,即你切换主项目分支的时候,关联的子仓库也会同步切换
缺点
- 需要学习新的操作指令,git subtree有自己的一套指令
- 指令的复杂度比较高,相比于常规的操作指令,其更加的复杂;
- 子仓库操作的时候,由于关联了其他仓库,因此指令的响应速度没有单仓库快
- 子仓库的分支管理比较麻烦
指令列表
git subtree add --prefix=<prefix> <commit>
git subtree add --prefix=<prefix> <repository> <ref>
git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <ref>
git subtree merge --prefix=<prefix> <commit>
git subtree split --prefix=<prefix> [OPTIONS] [<commit>]
几条指令而已,再复杂也只有这么几条,所以相对于他的优点来说,上面说的缺点也就只是微乎其微了
操作说明
在码云上面创建了三个仓库用于做测试:praent(主仓库), childa、childb(子仓库)
-
引入子仓库
git remote add -f <仓库别名> <仓库地址> #如 git remote add -f childa https://gitee.com/pengfeilu/subtree-childa.git
这条指令不是git subtree的指令,只是为了方便后续的操作,把仓库引入到当前项目作为一个remote,并且给其取了个别名,后续的操作 childa = https://gitee.com/pengfeilu/subtree-childa.git
- 添加子仓库
git subtree add --prefix=<prefix> <repository> <ref> #如: git subtree add --prefix=childa childa master --squash #等价于: git subtree add --prefix=childa https://gitee.com/pengfeilu/subtree-childa.git master --squash
- 第一个参数
--prefix=modulea
指明子仓库放在父仓库存放的路径,--prefix
可以使用大写的-P
简写;这里是放在childa目录,也允许设置多级目录,如childa/a/b/c - 第二个参数
modulea
子远程仓库地址,由于上面一个做了关联,所以这里直接使用的别名,直接使用远端地址也是可以的 - 第三个参数
master
: 添加的子仓库的分支名称,这里使用标签或者commitId也是可以的 - 第四个参数
--squash
: 只将本次操作在主仓库生成一条commit记录,子仓库的历史记录并不会合并进来,可以根据实际情况使用
- 推送更新到子仓库
git subtree push --prefix=<prefix> <repository> <ref> #如:git subtree push --prefix=childa childa master #等价于: git subtree push --prefix=childa https://gitee.com/pengfeilu/subtree-childa.git master
参数说明等价于上面添加的参数
- 更新子仓库代码
git subtree pull --prefix=<prefix> <repository> <ref> #如:git subtree pull --prefix=childa childa master --squash #等价于: git subtree pull --prefix=childa https://gitee.com/pengfeilu/subtree-childa.git master --squash
测试:
- 子仓库childa添加文件并上传
- 主仓库基于分支获取最新的 childa的提交
- 子仓库childa添加文件并上传
-
对已有的项目目录进行拆分
以上的操作是我们从一开始就考虑好那一部分是子仓库,从一开始就将子仓库定义好了,这种场景比如在RPC(thrift、grpc等)相关框架自动生成的代码,那么这个子仓库就是用于保存自动生成的那部分代码,而且从一开始就有了,客户端和服务端直接引用就好了;但是业务开发,可能会出现做到一半,发现某一部分可以抽离出来作为一个公共的子模块,那该怎么做呢?git subtree split --prefix=<prefix> [OPTIONS] [<commit>]
如当前我的项目下有个文件夹
myfile
,我需要将其剥离成一个新的子项目childb
- 第一步,将myfile目录剥离成一个新的分支
git subtree split -P myfiles -b childb
- 在主项目的同级目录创建一个用于保存这个分支的文件夹
# 创建文件夹 mkdir ../subtree-childb # 切到对应的文件夹 cd ../subtree-childb/ # 初始化成一个git仓库 git init
- 将剥离出来的分支并到当前目录来
# ../subtree-praent 为主仓库的路径 # childb 为第一步剥离出来的分支名称 git pull ../subtree-praent childb
- 将新的仓库关联到远端仓库
git remote add origin https://gitee.com/pengfeilu/subtree-childb.git
- 推送到远端代码
git push -u origin +master
到此!git subtree的常规操作都覆盖了,具体可以在实际使用中去体会!
- 第一步,将myfile目录剥离成一个新的分支