采用 GitSubtree 的方式来共用多个 project 会共用的 sourceCode,不过 subtree 的数据似乎不多,查到的数据都没有顺利的让我完成我的 subtree 情境,所以自己参考数据并且尝试成功后,就写一篇 blog 来记录一下,希望 subtree 以后会更方便用
写在前面的前面 (2017/05/06更新)
经过了大概几个月的使用,近日决定放弃用 Subtree 改回用 SubModule,因为遇到了一些问题,最严重的是两个使用同一个 Subtree 会莫名的不同步,
不同步的意思就是一边更新上去了,并确认在 Github 上已经是新的,另外一边 subtree pull 回来的竟然不是最新的,这样就算了,此时把 subtree push 回去,
要发 PR 还会发现竟然会把之前修改的盖回去!!这也不是第一次遇到,第二次遇到,所以决定要放弃 Subtree 改回去 Submodule ,应该稳定运行是没有问题,虽然麻烦了一点。
其他的问题例如:
1. 你在 Subtree 的 repo 会看到许多的 commit ,那些 commit 是使用 subtree repo 的人所造成的,并不一定真的对 subtree repo 里面的文件有影响
2. Main project 把修改 push 回 subtree repo 后,发 PR 竟然文件没有任何变更,这样就算了,直接 clone subtree repo 下来,下 merge 竟然会说是 unrelated repo... 真的是 WTF
也许是我 git 命令及使用方式不熟悉,所以才会有此问题,但现阶段来说研究这个浪费时间,所以决定改走回 submodule 的老路。
不过如果你还是有兴趣试看看的话,欢迎继续往下看
写在前面
为什么会用到 SubTree 呢?因为之前同事有用了 SubModule 觉得麻烦,虽然目前 Git 的 GUI 工具其实应该都有支持了,但如果是操作的话,相较于 SubTree 来说是繁琐了一些。
最近共用 Library Project 的需求再起(WPF & UWP)所以花了点时间找了替代 SubModule 的方案,看起来就只有 SubTree 啰~
看到一些地方也说建议大家可以用 SubTree 来替代 SubModule
SubTree vs SubModule
当初的出发点只是要找一个替代品,没有仔细的想过这问题,也是被问到后才去查的,其实目的上当然是差不多,但使用的情境上,或者看命令的设计上还是有差距的
查到 这篇 觉得解释的不错,直接把原文节录出来
- submodule is a better fit for component-based development, where your main project depends on a fixed version of another component (repo). You keep only references in your parent repo (gitlinks, special entries in the index)
- subtree is more like a system-based development, where your all repo contains everything at once, and you can modify any part.
我是这么理解的
- SubModule 适合像是说所谓的共用函示库你是没有主空权的状况下,通常你用的都是一个固定版本,就是变动不那么频繁的函示库,在命令的使用情境上 SubModuel 的确有比较繁琐
- SubTree 就是比较像我们需要的情境,会跟着 Main Project 一起成长的与改变的,在命令上使用相对简单许多
关于 git subtree vs git submodule 命令的操作,可以看 这篇 ,在里面就可以看到使用 git subModule 的状况下,命令需要满繁琐的,而 subTree 就几个简单的命令就搞定了
But... 人生最想不到的就是这个But...不然我也不会想要写这一篇 blog 了 XD
就让我来说一下从 SubTree 从头开始和我遇到的状况吧~(命令详细的参数使用方式就麻烦自行去查,谢谢~)
一、分割原来的源代码
因为我们要共用的函示库本来是我们 main repo 的一个数据夹,所以第一个步骤就是要把这个数据夹切割出来成为另外一个 repo,而 git 就有提供了一个 git subtree split 的命令,就可以指定一个目录,把那个目录的东西切割到另外一个 branch 去,
接着就可以把那个 branch 的东西 push 到你远端的另外一个函示库专用的 repo 了。
然后就遇到第一个问题,在 split 后我看 split 出来的数据夹也没有任何东西 (ex: .git 的数据夹,没有 .git 数据夹就不会有原来的 commit log 了) 阿我这样到底要怎么 push 啦?XD 从 GUI tool 是可以看到我 split 出来的另外一个 branch没错,结果搞半天后,
发现因为那个是个 branch 所以索性就切换到那个 branch,然后就可以 push 了 囧
OK~ 解决
二、加入远端的 repo 为 subtree 并且 push 回去 main repo 后让 git subtree 命令也都运行正常
这边就是卡最久的地方,出乎意料的麻烦,不过也多学到了一些东西就是。
要加入一个另外一个 repo 当作自己的 Subtree 来源,首先当然就是要用 git remote add 来增加一个 remote 的来源,就让新增的这个 remote 叫 shareLibraryRepo 吧!
然后就是要把它加入自己的 main repo 当作 subtree 啦~ 这时候我用第一时间查到的命令就是用 git subtree add -P ShareLibrary shareLibraryRepo master 来把 repo 放到我的 ShareLibrary 的数据夹当作 subtree ,
一切都看起来很简单顺利,然后我就把 main repo 给 push 回去,这时候当然要自己测试一下这样测试一下别人拉回去后,如果要用 git subtree pull 可不可以正常运行啰!
所以我就弄了一个干净的 main repo ,一样加了一个叫 shareLibraryRepo remote 后,下了 git subtree pull -P ShareLibrary shareLibraryRepo master 的命令,要来更新看看,结果就遇到了
refusing to merge unrelated histories
的错误消息... WTF... 那时候以为中间那边弄错了,重试了一次结果还是遇到一样的问题... 囧
只好再去找数据,又花了点时间结果找到 Github 其实就有 这篇 在介绍 subtree merge 的方式,照着上面的步骤做后,成功了~ 用了干净的 main repo 后,可以正常的下 git subtree pull 的命令更新 shareLibraryRepo 的 repo 了!
不过... 事情当然没有这么简单... =.=
当我开心的在 main repo 做了一些修改,包含有修改到 ShareLibrary 的东西,所以 commit 完 main repo 后,想要把对于 ShareLibrary 修改的东西一样 push 回去,然后就开心的下了 git subtree push -P ShareLibrary shareLibrary master,然后就遇到
! [rejected]??????? 72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to ‘[email protected]:*‘
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. ‘git pull‘)
hint: before pushing again.
hint: See the ‘Note about fast-forwards‘ in ‘git push --help‘ for details.
的错误消息... 被 reject !!! WTF... 然后又查半天找不到什么好方式,然后我就转方式想说一不做二不休,想说用 force push 的方式,但很不幸的 git subtree push 并不支持 --force 的参数,那....有办法吗?答案是有的!请参考下列命令的方式
git push shareLibraryRepo `git subtree split -P ShareLibrary --ignore-joins`:master --force
利用的是 git push 的 chain command 的方式来达到,嗯~总算松了一口起,起码刚刚改的一堆东西还可以回去 shareLibrary 的 repo!
但是这不是个解决问题的方式,总不能每次都要 force push 啊!!所以又花了时间尝试了整合 subtree 的命令步骤,最后终于发现了关键的一个地方...直接讲结论...
1. 用 git subtree add 是对的!
2. 记得下完 git subtree add 后,一定要多下一个下面的命令!记得下完 git subtree add 后,一定要多下一个下面的命令!记得下完 git subtree add 后,一定要多下一个下面的命令!因为很重要所以说三次
git merge -s ours --no-commit --allow-unrelated-histories shareLibraryRepo/master
这行命令就是建立好 subtree 关系的关键步骤啊!!!(抱头)就差了这一个步骤,所以让我一开始会遇到 refusing to merge unrelated histories 的错误消息 >"
真的是雷!我其实不确定这到底是 bug 还是 feature (摊手)
总之在这之后,git subtree pull/push 都运行正常啦!(洒花)
王子和公主从此过着幸福快乐的日子
写在后面
尝试了 SubTree 的东西,大概有点心得
- main repo 有没有加入 subtree 对 main repo 的操作没有任何影响,就把它视为 main repo 的一步份即可,无须特别理会
- 换句话说修改到 subtree 里面的 sourcecode commit & push 回去 main repo 后,其他在同一个 main repo 的人不需要用 subtree 命令去 pull,就直接 pull main repo 即可,因为基本上它就是 main repo 的一部分
- 有影响的状况只有
- (1) subtree 的 repo 有更新,并且你想要 pull 回来的时候
- (2) 你有修改到 subtree 的东西并且想 push 回去的时候
- 要 pull / push subtree 的 repo 几个步骤
- (1) 加入 shareLibrary 的 repo 为 remote 的来源(这个只要加过一次即可)
- (2) 使用 git subtree 的命令来 pull / pull
- git subtree 仅有 add / pull / push / split / merge 几个 command 而已,其实没有太复杂
- split 比较特别,用途是将你目前 repo 的一个目录可以切成另外一个 repo,目的就是切割你原来 repo 的一个目录然后可以当作 subtree
- subtree 最好不要加到与原来 shareLibrary 相同的路径,不然原来的 commit 历史纪录会被一并推回去远端的 repo (这点我不确定是不是因为这样的缘故,不过我是这样解决的)
- 建议可以看看的连结:
- mastering subtree
- 里面有提到 git subtree 的命令,其实是包装好的一些命令的只是先帮你包装好而已
- 例如 git subtree pull/push 其实就跟上面看到的 force push 的是一样的,其实都是透过 chaine command 的方式做到,都会帮你串 git subtree split 的命令
- 例如 git subtree add 也是帮你做好了在那篇 Github 文章的几个步骤
- mastering subtree
最后也是要来说一下目前用 subtree 所觉得的缺点
- git subtree push 会花很久的时间,就像心得里面因为他会先 split 然后再 push,split 的时候就会计算那个数据夹的 commit 哪些是有关系的,通常都会花一段时间,目前这个还没找到解法
- 所以如果要比较快的 workaround 的方式,就是直接去修改 shareLibrary 的那个 repo,然后再用 git subtree pull 的方式拉回来,不过这样其实应该比较麻烦,所以就不要每次修改到 subtree 就都要 push 就好
- GUI 工具支持度不足,目前只知道 SourceTree 有支持,不过还好,就下操作而已
以上~ 感谢各位的观赏~ 我们下次再见~
2017/01/18 更新:
上面有说道 git subtree push 会花很久的时间,后来发现原来是有一个地方做的方式改变一下就不会了,如果你原本就照上面的方式使用 git subtree add 的命令,没有没有加其他的参数的话,应该就不会遇到我说的 git subtree push 的时候会很久的问题;
我会说会花很久的原因是因为我再下 git subtree add? 的时候多加了一个 --squash 的参数,这个参数可以让 subtree 加入的时候只会产生一个 commit ,但是看样子这样的缺点就是因为他没有完整的 subtree 的 history,所以会导致在 subtree push 的时候必须"重新"
检视一次纪录所导致,所以... 如果没有特别理由,还是不要用 --squash 噜~
原文:大专栏 [Git] 使用 Git SubTree 来共享函示库源代码
原文地址:https://www.cnblogs.com/petewell/p/11526585.html