浅析Git Subtree的原理与实际应用:git subtree是什么、子仓库与仓库共用、共用代码需求常规处理方式及为什么使用git subtree以及如何使用Git Subtree在多个Git项目间双向同步子项目

一、为什么需要使用Git Subtree

  关于子仓库或者说是仓库共用,git官方推荐的工具是git subtree。

  在实际的项目开发过程中,公共的代码或者模块是必定会出现的,为了不重复写相同的代码;普遍的做法就是将其抽取成一个公共模块,这个模块由不同的使用者引用。

  作为Java工程师,可能会选择将这一部分打包封装成一个jar,并且将其推送到Maven的私有仓库,各个使用者将其添加到pom文件即可;作为前端工程师,可能会选择将这一部分打包封装成一个自定义plugin,将其推送至npm的私有仓库,各个使用者去安装即可。

  但是有没有更好的方式呢?因为使用这种方式,对使用者来说,并不是透明的,当前可能已经更新版本了,但是我作为使用者并不知道当前已经更新了,知道更新了可能也不知道最新版本是多少,我还得通过咨询模块开发人员或者说到私有仓库去查看才能知道,那有没有更好的方式呢?

  接下来,就一起探讨一种更好的管理方式:git subtree。

二、共用代码的需求及处理方式

1、需求背景

  项目A与项目B存在公用模块,在项目A中修改Bug或增加新功能需要同步到项目B中,由于存在区别所以还不能完全copy

2、需求分析

(1)公用代码迁移出去独立的 git 仓库,供其他项目共享代码

(2)公用代码原本是什么样,抽取后也是什么样

(3)公用代码库是可以在不同项目间双向同步的而不是单向同步

(4)保留公用代码库的历史提交记录与双向同步记录

双向同步的例子:A项目中依赖了子项目B,最方便的方式自然是直接在A项目里改B子项目对应的目录里的代码,然后测试通过后,直接提交代码,这个更改也提交到B子项目的 Git仓库里。同时子项目B也可以单独提交到 Git 仓库,再在A项目里把子项目B的代码update。

3、现有方案

(1)Git Submodule:这是Git官方以前的推荐方案

(2)Git Subtree:从 Git 1.5.2 开始,Git 新增并推荐使用这个功能来管理子项目

(3)npm:node package manager,实际上不仅仅是 node 的包管理工具

(4)composer:暂且认为他是php版npm

  虽然 npm,composer,maven 等更侧重于包的依赖管理,以上几个方案都是能够做到在不同项目中同步同一块代码的,但没法双向同步,更适用于子项目代码比较稳定的情形

  Git Submodule 和 Git Subtree 都是官方支持的功能,不具有依赖管理的功能,但能满足我们的要求。Git Subtree相对来说会更好一些 。git v1.5.2以后建议使用git subtree

4、Git Subtree 好在哪里

  用一句话来描述 Git Subtree 的优势就是:

经由 Git Subtree 来维护的子项目代码,对于父项目来说是透明的,所有的开发人员看到的就是一个普通的目录,原来怎么做现在依旧那么做,只需要维护这个 Subtree 的人在合适的时候去做同步代码的操作。

  它是怎么做到的呢?后面会通过实操记录简单说下原理。

三、什么是git subtree?有啥优势?

1、git subtree可以将一个仓库作为仓库的子仓库

2、各个仓库之前的版本管理是相对独立的

3、对于使用者来说,是透明的,可能使用者根本都不知道有子仓库的存在

4、主子仓库的分支同步,即你切换主项目分支的时候,关联的子仓库也会同步切换

四、什么时候需要Subtree

1、当多个项目共用同一模块代码,而且这块代码跟着项目在快速更新的时候

2、把一部分代码迁移出去独立为一个新的 git 仓库,但又希望能够保留这部分代码的历史提交记录

五、Git Subtree的原理与实际应用

  首先,你需要2个主项目,我利用之前的demo项目、demo2项目,还要有一个被多个项目共用的subtree项目。我们通过简要讲解使用Subtree来同步代码的过程来解释Subtree的原理

1、初始化子项目subtree

  通过我之前的demo项目做下示例

cd demo项目的路径  
git subtree add --prefix=用来放subtree项目的相对路径 subtree项目git地址 xxx分支
PS D:\demo\vue-button-test> git subtree add --prefix=/subtree https://gitee.com/***/npm_test.git master
git fetch https://gitee.com/goloving/npm_test.git master
From https://gitee.com/goloving/npm_test
 * branch            master     -> FETCH_HEAD
error: Invalid path 'C:/Program Files/Git/subtree/README.md'
error: Invalid path 'C:/Program Files/Git/subtree/index.js'
error: Invalid path 'C:/Program Files/Git/subtree/package.json'
error: Invalid path 'C:/Program Files/Git/subtree/plugins/lib/button.vue'
fatal: C:/Program Files/Git/subtree: 'C:/Program Files/Git/subtree' is outside repository
PS D:\demo\vue-button-test> git subtree add --prefix=subtree https://gitee.com/goloving/npm_test.git master
prefix 'subtree' already exists.
PS D:\demo\vue-button-test> git subtree add --prefix=subtree https://gitee.com/goloving/npm_test.git master
git fetch https://gitee.com/goloving/npm_test.git master
From https://gitee.com/goloving/npm_test
 * branch            master     -> FETCH_HEAD
Added dir 'subtree'
PS D:\demo\vue-button-test>

  遇到3个报错:

(1)Working tree has modifications. Cannot add. 实际上工作树没有更改,解决:关闭终端,重新开启终端即可

(2)--prefix=/subtree 应该是相对路径,不是绝对路径,所以写 /subtree 匹配到 C:/Program Files/Git/ 去了

(3)--prefix=subtree 刚开始以为必须新建这个目录,发现不是的,新建会报错,删除即可

  这样的命令,把subtree即npm_test项目的代码下载到--prefix所指定的subtree目录中,并在demo项目里自动产生一个commit(就是把npm_test目录的内容提交到demo项目里)

  我们再用一个demo2项目做同样的操作

2、像往常一样更新代码

  大家在demo项目里各种提交commit,其中有些commit会涉及到subtree目录的更改,正如前面提到的,这是没任何关系的,大家也不会感受到有任何不一样。

  比如我们在 npm-test 项目里button按钮后随便加个文字:subtree,看下图 demo 项目生效了

  然后我们把更改的所有内容 git 提交到 demo 项目仓库,我们可以看到下面全部的 commit 记录

 

3、提交更改到子项目的Git服务器

  关键的地方来了: 当维护这个demo项目 subtree 的人希望把最近这段时间对demo项目subtree目录的更改提交到subtree对应的npm_test项目的 Git 服务器上时,他执行一段类似于这样的命令:

cd demo项目的路径  
git subtree push --prefix=用来放subtree项目的相对路径 subtree项目git地址 xxx分支
PS D:\demo\vue-button-test> git subtree push --prefix=subtree https://gitee.com/***/npm_test.git master
git push using:  https://gitee.com/goloving/npm_test.git master
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 384 bytes | 384.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.3]
To https://gitee.com/goloving/npm_test.git
   adbf133..bbe8c7b  bbe8c7b22181acc371c7118f6392fafa9448ce4f -> master
PS D:\demo\vue-button-test>

  Git 会遍历所有的commit,从中找出针对subtree目录的更改,然后把这些更改记录提交到subtree对应的npm_test项目的Git服务器上

  然后我们就可以看到 npm_test 项目上多个对button更改的记录

4、更新子项目新的代码到父项目

  现在subtree npm_test项目有新代码了,demo2项目也想使用这些新代码,维护demo2项目这个Subtree的人只要执行

cd demo2项目路径
git subtree pull --prefix=subtree项目的路径 subtree项目git地址 xxx分支
PS D:\其他\big-data-view1>  git subtree pull --prefix=subtree https://gitee.com/goloving/npm_test.git master
Working tree has modifications.  Cannot add.
PS D:\其他\big-data-view1>  git subtree pull --prefix=subtree https://gitee.com/goloving/npm_test.git master
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
From https://gitee.com/goloving/npm_test
 * branch            master     -> FETCH_HEAD
Merge made by the 'recursive' strategy.
 subtree/plugins/lib/button.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
PS D:\其他\big-data-view1>

  当有未提交记录时会报这个错误:Working tree has modifications. Cannot add. 解决:将未提交记录全部提交即可

  接下来我们就可以看到 demo2 项目里的 button 按钮就更新为最新的了

  这样就可以将demo2项目里subtree目录里的内容更新为npm_test项目xxx分支的最新代码了。

posted @ 2023-02-01 21:44  古兰精  阅读(2693)  评论(0编辑  收藏  举报