学习背景
问题实例(真实诉求)
一个工具类库,有以下两个目标:
- 不与业务相关,便于复用
- 方案:maven 作为依赖引用,使用方便,但更新繁琐
- 优点:使用方便
- 缺点:1. 不易更新,发现工具类不符合需求时,直接要更改源仓库处理,update 使用;2.gradle 引用后,在没有 download 源码的情况下,看不到注释,注释对于一个工具类还是很重要的
- 方案:maven 作为依赖引用,使用方便,但更新繁琐
- 需要快速迭代更新
- 方案:直接引用 lib 源码,更新便捷实时,但不适合多项目的使用
如果直接使用 Git 管理这个lib 添加到这个项目来呢,单独维护。可能就会出现每一次更新 repo 就要更新多个 repo 的情况。对于有很多开发协同者来说,简直是灾难。
git subtree 是什么?
git subtree 可以实现一个仓库作为其他仓库的子仓库,能够相对较好的解决上面两难问题。
subtree
有个和 git subtree 概念类似的功能 git submodule
,虽然都没有用过,但目前来看 git subtree
比 git submodule
更加强大,是可以替代 submodule
的方案。
-
git submodule
在本地可能存在多个 git 代码仓库 -
git subtree
只有一个代码库,也就是说在项目内部依赖外部独立项目的时候,是完全无感知的操作。
subtree 的作用就是可以实现一个仓库作为其他仓库的子仓库,对于主项目来说,另一个项目只作为主项目的一个子目录而存在。
图片来自 :https://segmentfault.com/a/1190000012002151
使用
$ git subtree -h
usage: git subtree add --prefix=<prefix> <commit>
or: git subtree add --prefix=<prefix> <repository> <ref>
or: git subtree merge --prefix=<prefix> <commit>
or: git subtree pull --prefix=<prefix> <repository> <ref>
or: git subtree push --prefix=<prefix> <repository> <ref>
or: git subtree split --prefix=<prefix> <commit>
-h, --help show the help
-q quiet
-d show debug messages
-P, --prefix ... the name of the subdir to split out
-m, --message ... use the given message as the commit message for the merge commit
options for 'split'
--annotate ... add a prefix to commit message of new commits
-b, --branch ... create a new branch from the split subtree
--ignore-joins ignore prior --rejoin commits
--onto ... try connecting new tree to an existing one
--rejoin merge the new branch back into HEAD
options for 'add', 'merge', and 'pull'
--squash merge subtree changes as a single commit
在主项目中添加子项目
创建一个git repo GitStudy
创建一个 lib repo commonToolsCopy
使用 subtree 命令,将 tools 放到 gitStudy 目录下(作为lib)
下面 commonTools
$ git subtree add --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master --squash
--squash
参数表示不拉取历史信息,而只生成一条 commit 信息。
=
后面的 commonTools 是子项目 clone 后在本地的目录名,可以是多级,比如 lib/xxx
master
指的是子项目的分支名
上面命令执行后,就可以将 commonTools 仓库中 master 上的更新更新到本地
$ git subtree add --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master --squash
git fetch http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master
warning: no common commits
remote: Enumerating objects: 242, done.
remote: Counting objects: 100% (242/242), done.
remote: Compressing objects: 100% (99/99), done.
remote: Total 242 (delta 44), reused 242 (delta 44)
Receiving objects: 100% (242/242), 51.03 KiB | 4.25 MiB/s, done.
Resolving deltas: 100% (44/44), done.
From http://git.ninebot.cn:8888/tingtingtina/commontoolscopy
* branch master -> FETCH_HEAD
Added dir 'commonTools'
之后会生成两条日志信息
$ git log
commit a0a743a92c1fc6eddc204abbcfbab0ac530d3c46 (HEAD -> master)
Merge: a8b13f3 23e60fb
Author: liting <xxlt0310@163.com>
Date: Thu Apr 15 17:25:46 2021 +0800
Merge commit '23e60fbfdaa2ce6e63b4cc666ed59cd373d33c1e' as 'commonTools'
commit 23e60fbfdaa2ce6e63b4cc666ed59cd373d33c1e
Author: liting <xxlt0310@163.com>
Date: Thu Apr 15 17:25:46 2021 +0800
Squashed 'commonTools/' content from commit d25f3bd
git-subtree-dir: commonTools
git-subtree-split: d25f3bd8f7ab886c8806f031a17d4a841671a21a
commit a8b13f3d1cba3c5402821d613b9e0ab29f028998 (origin/master)
Author: liting <xxlt0310@163.com>
Date: Thu Apr 15 17:13:05 2021 +0800
init
添加之后 commonTools 就是主项目的一个普通文件夹,如果这时候 commonTools 内容更新之后,正常 git push
即可,子项目对于主项目来说完全是透明的。
推送到子项目仓库
在主项目 git push 之后,对子项目修改的内容是不会直接更新到子项目自身的
需要执行 git subtree push
git subtree push --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master
子项目仓库更新
如果单独对子项目做了更新,主项目就需要手动更新下了
$ git subtree pull --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master --squash
执行之后,仍然会在主项目中生成相关的 commit 记录
在 subtree 相关命令经常会用到 <repository>
每次都写地址还是比较麻烦,这里可以用 git remote
命令简化写法,为这个远程地址定义一个 “别名”
$ git remote add utils http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git
比如使用 subtree push 的时候就可以使用如下命令
$ git subtree push --prefix=commonTools utils master
移除子项目(切换子项目分支)
在添加 subtree 的时候是指定了分支的,如果要切换分支直接移除 subtree,重新加入子项目的分支
git rm <subtree>
git commit
git subtree add --prefix=<subtree> <repository_url> <subtree_branch>
验证1:其他人 clone 该项目对 sub repo是无感知的
验证2:在主項目中修改 sub repo 的内容 push,在 app 中会形成 commit 在 sub repo 中不会有 commit
验证3:使用一下命令,可以push 到 sub repo 中 推送修改到源仓库
git subtree push --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master
验证4:直接在 sub repo 项目中更新内容
那么 main repo 需要还是需要 pull 一下 从源仓库拉取更新
git subtree pull --prefix=commonTools http://git.ninebot.cn:8888/tingtingtina/commontoolscopy.git master --squash
非原理性提示 不加 --squash 不可以,
在 pull 的过程中可能需要处理冲突
验证5:修改的内容同时包含主项目和子项目, 使用 git push 再使用 git subtree push 会做挑拣,但如果混杂的内容很多的话,这个过程也会变得复杂,so 建议 尽可能分开提交,减少这种冲突。
参考
https://segmentfault.com/a/1190000012002151
http://einverne.github.io/post/2020/04/git-subtree-usage.html