使用git subtree有一段时间了,主要用来解决多个项目共同使用相同代码的同步问题,特意简单整理了一下相关知识点以及使用过程中遇到的问题。
git subtree是什么?
git subtree 可以实现一个仓库作为其他仓库的子仓库。
subtree的核心思想与能做的就只有同步项目文件。
- 假如有P1 、P2两个项目,两个项目存在共用的代码,将共用的代码独立为新的git仓库——share项目。
- 当你对P1/P2项目操作git clone或者git pull的时候,你拉取到的是整个P1/P2项目,包括share在内,share对于父级的主项目来说相当于是个普通目录;
- 当你在P1/P2项目修改了share里的内容后执行git push,修改的share文件将像其他普通文件那样push到P1/P2项目上。
subtree本质就是把子项目目录作为一个普通的文件目录,对于父级的主项目来说是完全透明的,真的就是个普通目录,原来是怎么操作现在依旧是那么操作,就像操作主项目中其他文件一样的 add commit。
什么时候需要 subtree ?
1、当多个项目共用同一坨代码,而这坨代码跟着项目在快速更新的时候
2、把一部分代码迁移出去独立为一个新的 git 仓库,但又希望能够保留这部分代码的历史提交记录。
如何使用git subtree?
1.确保各个项目已经添加这个 remote(可选,方便后续用别名代替)
// git remote add
git remote add share http://xx.git
2.关联subtree(只需要执行关联操作git subtree add即可,无需对share项目执行git clone)
// git subtree add --prefix= <分支> --squash
git subtree add --prefix=src/share share master --squash // 假设将share放在src目录下的
- --prefix之后的=等号也可以用空格
- --squash意思是把subtree的改动合并成一次commit,这样就不用拉取子项目完整的历史记录。如果不加 --squash 参数,主项目会合并子项目本身所有的 commit 历史记录,加上 --squash 参数是把子项目的记录合成一次 commit 提交到主项目,这样主项目只是合并一次 commit 记录。
3. 提交更改到子项目
// git subtree push --prefix=
git subtree push --prefix=src/share share master
高阶:每次push命令都会遍历全部的commit,当你的项目越来越大,commit的数上来的时候,等待时间就会很长。--rejoin 避免了遍历全部commit的问题.
//git subtree split --rejoin --prefix= --branch <临时branch>
git subtree split --rejoin --prefix=src/share --branch srcTemp
//git push srcTemp:master
git push share srcTemp:master
4.更新子目录:
// git subtree pull --prefix= <分支> --squash
git subtree pull --prefix=src/share share master --squash
参考文章:
用 Git Subtree 在多个 Git 项目间双向同步子项目
Git Subtree的使用
git subtree 要不要使用 –squash 参数
使用 --squash 参数
- 就是把 subtree 子项目的更新记录进行合并,再合并到主项目中:subtree add 或者 pull 操作的结果对应两个 commit, 一个是 squash 了子项目的历史记录, 一个是 Merge 到主项目中。
优点:主项目的历史记录看起来还是比较整齐的。
缺点:在子项目需要 subtree pull 的时候,经常需要处理冲突,甚至每次 subtree pull 的时候都需要重复处理同样的冲突。
原因:subtree add/pull 操作中,需要用到 merge,而 merge 顺利进行的前提, 是要有相同的 parent commit。原子项目历史记录被合并后就消失了,相当于一个“新”的提交。 下次再进行 add/pull 时,新添加的内容找不到“上一次的修改”, 于是在更新 subtree 内文件的时候,就会提示冲突,需要手工解决。
不使用 --squash 参数
优点:子项目更新的时候,subtree pull 很顺利, 能够自动处理已解决过的冲突。
原因: 原子项目的历史复制到了父项目中, 下次再进行 add/pull 时,新增的 commit 能够找到“上一次的修改”, 那么他会像在子项目中逐个 am patch 那样更新 subtree 下的内容, 不会提示冲突。
缺点:子项目的更新记录“污染”了主项目的。
总结
是否使用 squash 都是可以的, 但需要在开始阶段作出选择,并 一直坚持下去 。 如果一会儿用一会儿不用,得到的不是两者的优点,而是两者的缺点之和。
既希望能够比较顺利的更新子项目, 又不希望子项目的历史记录直接合并在主项目中,请参考文章:
Git subtree 要不要使用 –squash 参数
git subtree使用过程中的问题&解决方案
问题1:执行提交操作不成功
$ git subtree push --prefix=src/share share master
! [rejected] fc1de0e69c29fa1269feef734813f7889141859f -> master (non-fast-forward)
error: failed to push some refs to 'http://xxxx.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
解决方案:
// git push `git subtree split --prefix=Path/to/subtree master`:master --force
git push share `git subtree split --prefix=src/share master`:master --force
问题2:执行了以上命令,又出现新的问题
$ git push share `git subtree split --prefix=src/share master`:master --force
To http://xxx.git
! [remote rejected] fc1de0e69c29fa1269feef734813f7889141859f -> master (Git:You are not allowed to force push code to a protected branch on this project.)
error: failed to push some refs to 'http://xxx.git'
解决方案:
进入git页面,按照以下路径,点击un-protext按钮解除保护,再重新执行上面的操作
project settings->protected branches-> click un-protect.g
问题3:执行更新操作不成功
$ git subtree pull --prefix=src/share share master
Working tree has modifications. Cannot add.
解决方案:
项目有代码没提交,需要提交后才能执行pull
参考文章:
Git subtree - subtree up-to-date but can't push
Heroku deployment without the app being at the repo root (in a subfolder)
You are not allowed to push code to this project....! [remote rejected] master -> master (pre-receive hook declined)
原文:
git subtree相关问题
推荐文章:
前端开发之走进Vue.js(入门知识点)
快速掌握vue组件间的通讯
快速理解Vuex
如何在Vue+Webpack下配置Stylelint
Highchart属性笔记