一文搞懂git仓库共用的秘诀,让您优雅的管理项目。

在管理前端项目时,我们都会遇到这样的场景(项目使用git进行管理):

项目的业务代码和公共组件代码耦合度过高,当有新的项目时,我们通常会把公共组件从上一个项目进行拷贝,或者直接fork上一个项目作为新项目的仓库。

当发现公共组件出现问题,并做修改后,我们还要将组件在两个项目之间来回拷贝。

这就会出现一些问题,比如,来回拷贝比较耗时,而且不易管理,当再有其他项目时就会出现滚雪球的效应,因为公共的组件不只一个,一个组件变动了,为了同步最新的代码,往往每个项目都要改正一遍。

再者,如果公共代码没有规范的进行管理,每个项目组成员都可以改,那么会出现公共代码改错了的情况。

为了更好的管理前端项目,所以我整理了实现思路和落地方案供大家参考。

解决方式就是要把公共的代码抽取出来,并重构公共代码组件,提供业务需要时必须的组件参数,尽量做到组件和业务代码解耦。

具体做法就是为公共代码创建一个独立的仓库,在项目使用时通过拉取子仓库,并嵌入到项目中作为一个文件目录使用。

这样做的一个核心思想就是能够将远程仓库的代码拉取到项目中,并让开发人员像使用本地代码那样来使用远程仓库,能够做到本地代码调试。正好在git中提供这样的功能 即:git subtree git sumodule

使用git subtree

git subtree是一个用于管理多个项目仓库的工具。它可以将一个仓库作为一个子目录添加到另一个仓库中,这个子目录作为一个普通的文件目录嵌入到主仓库中,而不需要使用子模块或子树合并。

简单来说,就是把子仓库复制了一份嵌入到主仓库中使用。这样,就可以在多个项目中共享和同步一些公共的文件或组件,而不需要使用子模块或者复制粘贴的方式。

A项目中添加子树

现假如我有公共仓库的地址为:https://192.168.0.101/app/app-common.git 简称 common 仓库,也是作为子仓库供各个项目使用。

现有2个项目 A、B项目要common仓库进行引入,基本语法如下:

首先给各个项目添加远程子仓库的地址,需要先使用git remote add命令来添加一个远程仓库,作为子树的来源。

git remote add common https://192.168.0.101/app/app-common.git

这样,就可以把这个仓库命名为common,以便后续的操作。可以使用任意的名字来代替common,只要不和其他远程仓库的名字冲突即可。

接着,需要使用git subtree add命令来把这个远程仓库(common仓库)的指定分支的内容添加到你的当前仓库的指定目录下(当前仓库指的就是A项目或B项目),作为一个子树。

例如,如果想要把common子仓库的master分支的内容添加到A项目的components目录下,可以执行以下命令:

# 进入到A项目路径
cd 【A或B项目的根路径】
# 添加子树到项目中
git subtree add --prefix=【将子仓库存放到A或B项目中的相对路径】 【common项目git地址】 【common项目的分支】

# 使用案例
git subtree add --prefix=src/components common master

这样,就可以在A项目仓库中的src/components目录下引入这个远程仓库的master分支的内容,作为一个子树。可以根据具体需要,修改–prefix和分支名参数。

需要注意的是:上述命令需要做的前提是要保证A项目没有变更,就是本地的修改没有提交。否则会出现添加子树报错的情况,所以,要把未提交的数据回滚或提交上去才能使用上述命令。

A项目中修改子仓库并提交

如果我们想在A项目中修改子仓库的内容也没问题,修改后直接就可以在当前项目中把子仓库修改的内容提交到子仓库中,需要使用如下命令

git subtree push --prefix=src/components common master

上面的命令是将子仓库修改的内容提交到子仓库的master分支下,当然,这个master分支 可以变更名称,比如下面:

git subtree push --prefix=src/components common master-1

这个命令是在子仓库common中重新生成一个master-1分支,包含了你过去对src/components 所有的更改记录。

提交到子仓库的master-1分支依然可以合并到master分支上。但是为了安全,一般来说都是由项目管理员去子仓库上直接修改,尽量不要在项目中直接修改子仓库。因为我们刚刚说了,子仓库common就是一个公共的仓库,不允许随便修改的,而git subtree 正式适合这种不会频繁修改子仓库的场景中。

为了提升push 效率,我们也可在push之前 执行如下命令,目的是重新split出一个新起点,每次提交子树的时候就不会从头遍历一遍了。

git subtree split --prefix=src/components --rejoin
git subtree push --prefix=src/components common master-1

git subtree split --rejoin --prefix=src/components--branch master-1
git push common master-1:master
上面的两个命令是使用git subtree的高级用法,它们的目的是把当前仓库中的src/components目录的内容推送到另一个远程仓库的master分支,作为一个独立的仓库。这样,就可以把公共组件从当前仓库中分离出来,以便其他项目可以使用。这两个命令的具体含义如下:
git subtree split --rejoin --prefix=src/components--branch master-1:这个命令会把当前仓库的src/components目录的内容分离出来,作为一个新的分支master-1。这个分支只包含src/components目录的历史记录,而不包含当前仓库的其他内容。–rejoin参数的作用是让您的当前仓库的HEAD指向这个新的分支,以便后续的操作。
–prefix参数指定了想要分离的目录,–branch参数指定了想要创建的分支名。
git push common master-1:master:这个命令会把当前仓库的master-1分支的内容推送到另一个远程仓库的master分支。这个远程仓库的名字是common。

A项目拉取子仓库最新内容

刚刚我们说了,如果公共组件发生了修改,那么需要同步到其他项目中,使用如下命令进行拉取子仓库的最新内容:
git subtree pull --prefix=src/components common master

A 项目 删除子仓库的内容

如果我们想在项目中删除子仓库的内容,可以有两种方式,一种是直接手动删除文件夹,一种是使用命令:
git rm -r src/app-common

GIT SUBTREE的使用总结

其实使用git subtree 就这么简单,但是要清楚其原理。git subtree 是官方推荐使用的方式,其优点就是不增加额外的文件,管理和更新流程简洁,对于项目中的其他成员透明。项目成员可随意在本地进行代码调试。
其原理是使用git merge命令将子仓库的分支合并到父仓库的子目录中,使用git filter-branch命令将子目录的修改过滤出来,推送到子仓库的分支上。
Git subtree 一般用在:想要复制一个外部仓库的代码,或者偶尔拉取更新,而不需要频繁地修改和推送子仓库时。

使用 git submodule

相比git subtree, 对于刚刚的项目场景,还可以使用 git submodule 命令,这里我们简单提一提就可以了,使用方式比较简单。
git submodule的概念是:将一个仓库作为一个子模块添加到另一个仓库中,作为一个特殊的文件类型,只记录子仓库的提交哈希值。
其优点是:可以在父仓库中修改子仓库的代码,可以在子仓库中单独查看子仓库的修改记录,相当于在一个单独的仓库内,对外层父仓库不可见。
缺点是:需要额外的步骤来初始化和更新子模块,产生.gitmodules文件记录子模块的信息,删除子模块步骤繁琐。
git submodule的原理是:使用git clone命令将子仓库的地址添加到父仓库的.git/modules目录中,使用git update-index命令将子仓库的提交哈希值添加到父仓库的索引中,作为一个特殊的文件类型。

在主仓库项目内建立submodule子仓库:
主仓库内执行命令:
# (git submodule add 子仓库地址 主仓库建立submodule路径)
git submodule add http://192.168.5.153/app/common src/app-common
当引入submodule以后,submodule依旧是一个独立的项目,该引入路径下所有的pull、push等操作实则都是子仓库独立的git操作,主仓库只记载了相关commit id,并不管理子仓库的代码变动
其他人clone操作:src/common文件夹会被创建,但无内容
需要切换到子仓库路径(src/common)下执行相关命令进行初始化:
git submodule update --init
子仓库如需切换分支、更新代码等,直接在子仓库路径下git checkout 、pull、push。当子仓库有代码变动时,也同样需要单独进入子仓库路径进行pull 拉取操作。

Git subtree 和 git submodule

subtree操作更方便,但耦合更高。
submodule子仓库独立性高,耦合更低,但由于主仓库并不直接管理子仓库代码,导致每次相关代码变动都需要单独对主子两个项目进行git操作。
subtree 在主项目中是一个副本,而 submodule 是在主项目是一个子仓库的引用或链接地址。

Git-repo 命令

刚刚说的公共组件是必须要完整的应用到各个项目中使用,那么实际场景是:如果A项目只用 common 仓库中 两个组件,B项目中要用到 3个组件。这种情况公共仓库 和 各个项目仓库要如何分别管理呢?
答案就是使用git-filter-repo。
git-filter-repo是一个官方推荐的用于快速重写git仓库历史的小工具。它可以用来删除敏感数据、修改提交者信息、重命名文件或目录等操作。
对于我们这个场景,处理的核心思路就是:在子仓库common中,针对每个项目都在子仓库的完整的代码分支上克隆出一个单独的分支,这个分支属于各个项目,也就是一个项目对应一个公共仓库的分支,每个分支中包含各个项目中所需要的组件。那具体如何实现呢?关键点在于,如何在完整的分支上克隆出来,而不需要手动删除或添加。
这就需要用到git-filter-repo。它需要我们手动安装,一般来说,这样的场景的操作都要交给仓库的管理员来做,所以一般只需要一个人安装上即可。安装方式有很多中,如果你的电脑装了python3 就比较简单了,我们可以使用pip3 命令来进行安装,安装 命令如下:
python -m pip install --user git-filter-repo
安装完成之后,我们来检查一下安装的路径(python的scripts目录下如:C:UserssenduAppDataRoamingPythonPython310Scripts),要把这个路径添加到一文搞懂git仓库共用的秘诀,让您优雅的管理项目。中,也就是添加到环境变量中。它可以安装在 git --exec-path 指向的目录中,或放置在 $PATH 中的任何位置。
可以用如下命令来检查一下git的核心包路径,
git --exec-path
安装完成后,接下来就可以使用 git filter-repo 命令了。
假如我们现在子仓库完整的代码分支为master(公共组件的修改都要先同步到master分支),现在有个项目C想使用common仓库中的3个组件(1、2、3),而master 仓库一共4个组件(1、2、3、4),这就需要从master克隆出来一个适合C项目的公共组件,而这个公共组件只包括1、2、3这三个组件,那需要我们这样做:
1、管理员先在子仓库的本地以master分支为基础 创建一个临时分支 master-c-temp
2、过滤这个临时分支中的组件如下命令,将4组件过滤出去,只保留1、2、3:
git filter-repo --path 4 --invert-paths
上述命令中代表4 组件在子仓库下的路径,比如我有个目录结构是这样的:
一文搞懂git仓库共用的秘诀,让您优雅的管理项目。
我想过滤掉 initAndUpdateApp这个组件,那么我需要执行如下命令:
git filter-repo --path initAndUpdateApp --invert-paths
如果上述命令执行不成功,可以添加参数—force ,如下,这种方式是强制删除历史修改记录(其实没关系,我们有master分支),从当前最新开始
git filter-repo --path map --invert-paths --force
这样这个分支就只剩下了,除initAndUpdateApp之外的3个组件。
3、将这个分支重新推送到子仓库中,作为一个单独的分支使用:
# 给仓库添加新分支
git remote add master-c http://192.168.0.101/app/common.git
#提交
git push master-c --all
4、C项目使用subtree命令拉取子仓库的分支来使用。
5、当公共组件发生变化时,先合并到master分支,再重复上述步骤即可。
方案总结:
  • 在你的本地仓库中,运行 git filter-repo 命令,指定你想要过滤的路径、分支、标签等参数。
  • 运行 git remote add <new-origin> <new-repo-url> 命令,添加一个新的远程仓库,其中 <new-origin> 是你给新仓库起的名字, <new-repo-url> 是新仓库的地址。
  • 运行 git push <new-origin> --all 命令,将你的所有分支推送到新的远程仓库。
注意,使用 git-filter-repo 会改变你的仓库的历史记录,所以你需要强制推送你的更改,并通知其他协作者更新他们的克隆或分支。

总结

这篇文章可谓是非常详细了,从各个角度模拟了如何在不同应用场景下来管理项目,git命令还是非常强大,有很多命令这里面都没提到,尤其是 git filter-repo 命令的使用。因为我们主要讨论方案,具体使用还需要大量的实战,本文列出的实战方案基本解决了多个项目共享同一个仓库的需求。如果大家有更好的方式,可以发消息讨论哦~

往期文章推荐

计算机中的取模和补码运算到底是什么?

Mysql 索引加强版图解教程!为什么使用B+Tree的存储结构,看完这篇不懂都难。

前端多线程web worker详细教程,请保存~

太强了!nginx日志分析-生产环境实战!代码亲测有效

一款强大的nginx日志分析工具-goaccess分析

springboot+redisson分布式锁+定时job实现高性能、稳定的竞拍系统

nginx如何正确的配置才能获取到客户端的真实IP?多种场景说明清楚。

SpringBoot集成Guava RateLimiter 实现限流+源码解析

原文始发于微信公众号(小核桃编程):一文搞懂git仓库共用的秘诀,让您优雅的管理项目。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/216060.html

(0)
李, 若俞的头像李, 若俞

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!