git subtree
此文已由作者张磊授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
前言
目前对 git 仓库拆分的已有实现之一。这里 git subtree 并不是 subtree merge strategy,这两个不是一个东西。
准备工作
首先创建主仓库 subrepo-master,随意提交一次文本,接着拉取到本地
建立子仓库 subrepo,随意提交一次文本,同时准备多个分支进行任意提交备用。
操作
运行 git remote add subtree1 <url> 添加子仓库链接
添加子仓库 git subtree add --prefix=subtree/subtree1 subtree1 master,这里的 prefix 指向的是本地存放子仓库的目录, subtree1 是第一步添加的远端链接 ,master 是远端分支。添加完成后,就得到了第一个子仓库。
运行 git status 会发现没有本地修改,接着运行 git log,会发现多了一次提交信息,看起来 git subtree 在添加的时候会自动提交一次,现在可以选择把提交推送到远端。
commit 6cd935426f68cd670d50a7fb02065af9a0cded19 Merge: fbc33ec afdc5bf Add 'subtree/subtree1/' from commit 'afdc5bf559beacb08032e23d22a2beaa65d3ca9c' git-subtree-dir: subtree/subtree1 git-subtree-mainline: fbc33ec2739057789595c13d2313b87848bf25c0 git-subtree-split: afdc5bf559beacb08032e23d22a2beaa65d3ca9c
接着会做一些操作,模拟各种情况,如主仓库子仓库修改推送、子仓库远端拉取合并等。
主仓库子仓库修改推送到远端
首先对主仓库子仓库的文件做一些修改并本地提交(记住提交的 message),接着可以直接推送到远端。观察远端主仓库会发现修改顺利提交,subrepo 没有发生改变,这时候就可以体会出一点 subtree 的设计了。如果想推送子仓库,需要使用命令 git subtree push --prefix=subtree/subtree1 subtree1 master。再次观察远端,会发现修改被推送上去了,而且 message 一致。当然你可以选择不推送到 master 分支,随意找一个 git subtree push --prefix=subtree/subtree1 subtree1 master2 也是可以的。观察远端会发现多出一个 master2 分支。
远端子仓库有修改
对远端子仓库修改。然后拉取此次提交合并到主仓库。运行此命令拉取即可 git subtree pull --prefix=subtree/subtree1 subtree1 master2,这时候如果有冲突,就需要解决冲突,分别推送主仓库子仓库。然后观察远端仓库,会发现主仓库、子仓库都有了提交以及解决冲突的操作。
远端子仓库做了大量提交
希望从远端拉取合并到主仓库 git subtree pull --prefix=subtree/subtree1 subtree1 master2,再运行 git log 然后会发现主仓库的提交日志多了很多,是子仓库的提交日志 + 1 条,如果不希望子仓库污染主仓库的提交日志,可以使用 --squash 这个命令。git subtree pull --prefix=subtree/subtree1 subtree1 master2 --squash,就得到干净了日志了(一次是 squash 整理的提交,一次是合并),但是合并的时候有冲突需要解决。
本地子仓库合并远端子仓库 branch、tag、commitid
如果分支没有在本地拉取,则需要先 git fetch subtree1,接着再
git subtree merge --prefix=subtree/subtree1 subtree1/a // or git subtree merge --prefix=subtree/subtree1 v2.0 // or git subtree merge --prefix=subtree/subtree1 e1e9aef94c0ce5ede4716d3c1e48cc58c15b7ffe
本地子仓库改变分支
可以通过 git subtree pull --prefix=subtree/subtree1 subtree1 b 的方式,但是方式的缺点显而易见,需要手工再次合并代码等等。还有另一种方案,先删去目录,然后重新添加进来。
技巧
命令表
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>]
如果想要历史记录干净一点,可以使用 --squash,可用于 add, merge, push, pull 命令。但是加了 --squash 也有自己的问题,上面已做描述,这个最好一开始就统一使用一种。当然有人也有解决方案 https://stackoverflow.com/questions/9777564/git-subtree-pull-complications
缺点
合并策略的学习成本
返回历史版本稍显复杂
不要混合提交主仓库、子仓库代码
将子仓库推送到远端很慢,相当于对主仓库下某个子仓库的目录的提交记录进行分割,以找出子仓库的修改,这个过程有点慢,如果推送周期比较长倒是可以尝试。
需要添加多个远端,子仓库和远端没有地方存储映射关系。由命令 git subtree pull --prefix=subtree/subtree1 subtree1 master2 可以看出来,可以推送子仓库的代码到任意能推送的地方。
改子仓库的分支略坑
优点
拷贝主仓库后,可以直接使用
使用者可以不关注子树依赖,对使用者无感知,不需要子仓库权限
子树不像子模块会添加多余的文件,对仓库无污染
对子仓库的同步可交由单独的维护者
参考
https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt
https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/
https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree
https://medium.com/@porteneuve/mastering-git-subtrees-943d29a798ec
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 聊聊空状态设计
【推荐】 大数据在政府中的应用案例