Git 201 - Subtree Tutorial

Merge subtrees together and split repository into subtrees

Subtrees allow subprojects to be included within a subdirectory
of the main project, optionally including the subproject’s
entire history.

好久没想过git了,这东西跟五笔一样,已印在脑海里,都是属于随指尖而出的东西,不用经过大脑了。但subtree除外。

❉ subtree 解决的问题

在这个每种语言都已经有了属于自己的包管理工具的时代,其实git subtree的用途也不是很大。

Subtree是用来代替 submodule 的,那就顺带回想下,什么时候需要用到submodule。在没有包管理工具时代,如果想引入第三方的库,可以有两种方式:

  • 将第三方库托管到自己的源码下,统一管理。不方便是一定的,因为库有依赖关系,有版本限制,升级也很麻烦。
  • 使用git submodule,通过git的这种依赖关系,将源码下载到一个特定的目录,算是解除了依赖。

看似git sumdodule能解决问题,但它又带来了新的问题,如果这个库是你自己维护的,要给很多的项目用。在其中一个项目中,发现它有bug,如何修复。当然,你可以到这个库的git repo下升级,在项目中更新。但更好的做法不应该是随着项目一起把库的bug fix掉,然后提交吗?

如果你试着这样做了,可能接下来几天你都不大好过,submodule实在是太难用了,它会产生一堆的问题。好在 git 1.7.9.4 中引入了subtree 来解救。

总结来说,subtree的适合场景是项目中依赖其它的项目/库(有操作权限,很重要!),同时又没有合适的包管理工具,之前用submodule,现在就可以换成subtree了,它让你在项目中操作依赖库,就像直接在它的repo中操作一样方便。

❉ 试用 subtree

1
2
3
4
5
6
git subtree add -P <prefix> <commit>
git subtree add -P <prefix> <repository> <ref>
git subtree pull -P <prefix> <repository> <ref>
git subtree push -P <prefix> <repository> <ref>
git subtree merge -P <prefix> <commit>
git subtree split -P <prefix> [OPTIONS] [<commit>]

❉ 实例

  • 将另一个repo添加到当前项目yom的目录yoa
1
2
3
4
5
6
$ git subtree add --prefix=yoa git@github.com:lanvige/yoa.git master
# git fetch git@github.com:lanvige/yoa.git master
# From github.com:lanvige/yoa
# * branch master -> FETCH_HEAD
# Added dir 'yoa'

添加完后,yom/yoa目录中并未有.git目录。git commit history记录显示如下,[发现它是将两个git repo进行了合并]:

  • 修改yoa目录下文件,然后提交
1
2
3
4
yoa$ gp
# To git@github.com:lanvige/yom.git
# c97e75e..a7016c2 master -> master

可以看出,它是向yom repo进行的提交。

如果想向`yoa remote提交呢,则要:

1
2
3
4
5
6
7
8
9
$ git subtree push --prefix yoa git@github.com:lanvige/yoa.git master
# git push using: git@github.com:lanvige/yoa.git master
# -n 1/ 4 (0)
# -n 2/ 4 (0)
# -n 3/ 4 (0)
# -n 4/ 4 (1)
# To git@github.com:lanvige/yoa.git
# e810397..8b96835 8b96835051abe292dd6f7563885aaf001e6991c7 -> master
  • yoa repo起别名

上面,将yoa 子项目push时用了一个很长的全路径,如果多次使用,不是很好。可以尝试:

1
2
3
# 给 yoa remote repo 起名别名 yoar
$ git remote add yoar git@github.com:lanvige/yoa.git
$ git subtree push --prefix yoa yoar master
  • 如果yoa远程更新,将更新获取到本地yoa

我们在yoa的远程上添加b.txt文件。然后在yoam下进行拉取:

1
2
3
4
5
6
7
8
9
$ git subtree pull --prefix=yoa yoar master
# From github.com:lanvige/yoa
# * branch master -> FETCH_HEAD
# 8b96835..413fe34 master -> yoar/master
# Merge made by the 'recursive' strategy.
# yoa/b.txt | 1 +
# 1 file changed, 1 insertion(+)
# create mode 100644 yoa/b.txt
  • 将任意目录(举例 dist)发布到origin的gh-pages分支(dist必须commit到repo中)

虽然和假设的不大一样(dist 不应该被迁入的git中),但也能满足需求的将生产环境的代码发布到一个单独的分支中:

1
2
$ git subtree push --prefix=dist origin gh-pages
$ git subtree split --prefix=dist --branch yoa
  • 将任意目录独立成git repo

这个很帅的,试想,一个目录被从一个repo中独立中出,而且所有的操作记录都会保留。

1
2
$ git subtree split --prefix=dist git@github.com:lanvige/yoa2 master
$ git subtree split --prefix=dist --branch dist

REF::