0 实现功能
之前的项目是基于一个开源项目做的,后来开源项目一直在更新迭代,所以时不时需要把开源项目的新功能合入到自己的项目中,这样就出现跨项目仓库进行更新,而且这个开源项目也只是自己项目里边的一个子文件夹。
梳理一下功能需求:
- 公司项目A,把开源项目B以子文件夹的方式包含进来
- 开源项目B一直在维护并更新着
- 公司项目A需要把自包含开源项目B之后,开源项目B的修改合并进来
以上需求可以使用Git Subtree实现:
- 首先把项目A中以子文件夹存在的开源项目B独立成子项目
- 然后使用Git Subtree在需要的时候,直接把开源项目B的修改同步到项目A
1 参考资料
用 Git Subtree 在多个 Git 项目间双向同步子项目
git subtree教程
fork别人的项目保持与源项目同步更新的两种方式
2 Git Subtree
在介绍Git Subtree之前先说一下跟Git Subtree类似的一个功能Git Submodule,对于Git Submodule而言,在本地代码库可能存在多个Git代码仓库,并且有一个.gitmodule的文件记录着父项目添加的子module。
2.1 Sub module vs Sub tree 对比
- 对父项目的占用区别:对于父项目而言,如果使用 submodule 会在父项目中新增一个 .gitmodule 的文件来记录父项目添加的子 module,而使用 subtree 则会将子项目完整的克隆到父项目的一个文件夹中。
- 在 clone 子项目步骤上:使用 submodule 需要执行多个步骤,在拉取主项目后需要使用 submodule 命令单独更新 submodule;而使用 subtree 则只需要使用 clone 命令
- push 子项目:submodule 因为将子项目视为独立的项目,可以直接 push;使用 subtree 则需要手动进行对比
- pull 子项目:submodule pull 子项目后需要,在父项目再进行提交 git submodule update --recursive --remote;而使用 subtree 则直接 pull 即可
2.2 为什么要使用 git subtree?
git subtree 可以让一个 repository 嵌入到另一个项目的子目录中:
- 管理方便,对于项目中的成员无需关心额外的 git 工作流,使用最基本的 git 工作流即可
- 在拉取代码的时候,一行 clone 命令可以立即获得包括子项目在内的所有的项目文件,而不是像 git submodule 一样还需要额外的 update 命令
- git subtree 不会像 gitmodule 一样引入 metadata 文件来管理,git subtree 的使用对于项目中其他成员可以透明
- 子项目中的内容可以无缝的被修改,并且可以选择性同步到 upstream 中
2.3 使用Git Subtree跨仓库同步代码
下边就介绍下使用Git Subtree同步开源仓库的代码,为了介绍方便下边定义一些术语:
- 主项目A
- 需要同步的开源项目S
2.3.1 在项目父仓库中添加子项目
首先需要把开源项目添加到项目中,在使用 subtree 的时候也需要显式的指定需要添加的子项目。
git subtree add --prefix=<S项目的路径> S项目git地址 xxx分支 --squash
解释:
--squash
是将 subtree 的改动合并到一个 commit,不用拉取子项目完整的历史纪录- –prefix指定的是克隆后S项目所在的本地目录名,可以是相对路径
- xxx分支是开源项目S的分支
- 执行后会产生提交记录,可以使用
git status
和git log
查看,只有在执行了push后才会真正提交到主项目A中
2.3.2 更新子项目仓库
在依赖的开源项目S更新了,现在需要把S项目的更新合入到主项目A,执行如下操作:
git subtree pull --prefix=<S项目路径> S项目git地址 xxx分支 --squash
2.3.3 将更改推送到子项目仓库
平时如果要修改S项目的代码,可以直接进行修改,然后提交到主项目A中,但是如果想把对S项目的修改合入到S项目远端仓库,需要执行如下操作:
git subtree push --prefix=<S项目路径> S项目git地址 xxx分支
git subtree push
会将父项目中的提交每一次都进行提交,这会导致对于子项目来说无意义的提交信息,但是 git subtree 并没有提供类似 squash 的方式可以将多次提交合并成一次提交,但是 git subtree 提供了分支的特性,可以在父项目中将修改推送到某一个分支,然后在子项目中使用 squash merge 将修改合并到主干分支。
git subtree push --prefix=<S项目路径> S项目git地址 feature
这会在 foo 的仓库中创建一个叫做 feature 的分支。然后可以从 feature 分支合并回 master 分支。一旦最新的提交都合并到 master 分支,可以通过 pull 来更新
git subtree pull --prefix=<S项目路径> S项目git地址 master --squash
这会在主项目中创建另外一个提交,包括了子项目中所有的修改。这样的方式有一个缺点就是会在父项目中产生一些多余的提交信息。
2.3.4 subtree切换分支
使用 git subtree 加入到父项目的仓库,如果要切换分支,可以直接将 subtree 删掉,然后新加入子项目的分支即可。
git rm <subtree>
git commit
git subtree add --prefix=<subtree> <repository_url> <subtree_branch>
3 Git Upstream
另一种跨远端仓库同步代码的方法就是git upstream,但git upstream只能针对仓库,可以是git submodule添加的子仓库
3.1 什么是upstream?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZkBZKyWC-1611740317116)(en-resource://database/3978:1)]
只要把想要同步的远程仓库设置为本地仓库的 upstream,就可以很方便地进行同步操作了。
3.2 Git Upstream的使用
- 添加
$ git remote add upstream https://github.com/LambdaSchool/Original-Repo.git
- 查看
$ git remote -v origin git@github.com:MyName/My-Forked-Repo.git (fetch) origin git@github.com:MyName/My-Forked-Repo.git (push) upstream https://github.com/LambdaSchool/Original-Repo.git (fetch) upstream https://github.com/LambdaSchool/Original-Repo.git (push)
- 取消upstream
$ git branch --unset-upstream
- 合并到本地
git fetch upstream 分支 #拉取远端修改
git merge upstream/分支 --allow-unrelated-histories #把远端合并到本地仓库
- 上传到远端
git push origin