【Git教程】(十)版本库之间的依赖 —— 项目与子模块之间的依赖、与子树之间的依赖 ~

Git教程 · 版本库之间的依赖

  • 1️⃣ 与子模块之间的依赖
  • 2️⃣ 与子树之间的依赖
  • 🌾 总结

在这里插入图片描述

在 Git 中,版本库是发行单位,代表的是一个版本,而分支或标签则只能被创建在版本库这个整体中。如果一个项目中包含了若干个子项目,它们有各自的发布周期和属于自己的版本,那我们就必须要为每个子项目建立对应的版本库了。

对于主项目和子项目之间的关系,我们可以通过Git 中的 submodulesubtree 命令来实现。
请注意,subtree 命令是在1.7.11这一版本中首先被正式纳入Git 的。但该命令只是 contrib 目录下的一个可选组件。有些 Git 的安装包会自动包含的subtree 命令,而另一些则需要我们去手动安装。

子模块和子树这两个概念之间的主要区别在于:带子模块的主版本库只能发布模块版本库,而模块版本库的内容中带有子树的话,该模块版本库就被导入了主版本库中。


1️⃣ 与子模块之间的依赖

对于子模块来说,其模块版本库可以被嵌入到主版本库中去。为了实现这一点,模块版本库中的提交会以目录的形式被链接到主版本库中。

下面,我们通过下图来看看其基本结构。该图中有main 和 sub 两个版本库。在主版本库中,sub 目录将会与模块版本库相链接。这样,主版本库工作区的 sub 目录下就有了一个 完整的模块版本库。但事实上主版本库其实只是引用了模块版本库。为了实现这一目标,我们就得有一个名为 .gitmodules 的文件,以便用来定义各模块版本库所在的绝对路径。

[submodule "sub"]
path=sub
url=/project/sub

在这里插入图片描述

除了.gitmodules 文件之外,子模块的引用信息还会被被保存在 .git/config 文件中。该文件会在我们调用 submodule init 命令时完成存储,届时该命令会将从 .gitmodules 文件中读取的信息写入到 .git/config文件中。有了这样的间接配置,我们就可以在 git/config 文件对模块版本库的路径进行本地化调整了。

[core]
repositoryformatversion=0
filemode=true
bare=false
logallrefupdates=true
ignorecase=true
[submodule "sub"]
url=/project/sub

凭借上述信息,我们是不可能为主版本库中的每次提交都重现相应模块版本库的版本的。 也正因为如此,模块版本库中的提交才仍会被需要。这些都将会被存储在主版本库的对象树 中。下面我们来看看该对象树。其第三项 sub就是一个子模块,它可以被识别成 commit类型,随后的散列值引用的就是模块版本库中的提交 。

100644 blob le2bld1d51392717a479eaaaa79c82df1c35d442    .gitmodules
100644 tree 19102815663d23f8b75a47e7a01965dcdc96468c   src
160000 commit 7fa7elclbd6c920ba71bd791f35969425d28b91b sub

在这里,我们要将一个现有 Git 项目以子模块的形式嵌入到一个不同的项目中

  1. 链接目录
    如果我们想要纳入某个子模块,就必须调用 submodule add 命令,并指定该模块版本库的绝对路径与该模块所在的目录名:
    > git submodule add /global-path-to/sub sub
    这样一来,模块版本库就会被完整地克隆到指定目录中(并且它也会创建属于它自己 的.git 目录)。此外,主版本库中的.gitmodules 文件也将被同步创建或更新。
  2. 在 config 文件在注册子模块
    除此之外,新的子模块还需要被注册到.gitconfig 文件中。我们可以通过 submodule init 命令来完成这件事。
    > git submodule init
  3. 选择子模块的版本
    该模块版本库的工作空间最初会被设置为默认分支的 HEAD。如果我们想要子模块中 的另一提交,就需要用checkout 命令来选择一下相应的版本。
    > cd sub
    > git checkout v1.0
  4. 将该 gitmodules 文件和子目录添加到提交中
    当我们添加一个子模块时,主版本库中的.gitmodules 文件就会随之被创建或更新。然
    后,我们就必须要将其添加到提交中去。此外,子模块所在的新目录自然也要添加。
    > cd
    > git add .gitmodules
    > git add sub
  5. 做一次提交
    最后,我们需要在主版本库中做一次提交。
    > git commit -m "Submodule added"

如果我们克隆了一个带子模块的版本库,就必须调用一下 submodule init 命令。该命令 会将.git/config 文件中各子模块的 URL 传送过来。之后,我们就可以调用 submodule update 命令来克隆模块版本库所在的目录了。

克隆一个带子模块的项目
当我们克隆一个带子模块的版本库时,最初在工作区中创建的只有主版本库。其子模块必须要进行显式的初始化和更新。

  1. 初始化子模块
    首先,我们必须要用submodule init命令来完成子模块的注册。
    > git submodule init
  2. 更新子模块
    待该子模块在完成Git 的初始化配置之后,我们就可以通过 submodule update 命令来下载完整的子模块了。
    > git submodule update

我们可以用 submodule status 命令查看子模块中被引用提交的散列值。其中如果存在标签的话,也会以括号的形式显示在输出的结尾处。

> git submodule status
091559ec65c0ded42556714c3e6936c3bla90422 sub(v1.0)

在这里,Git 往往引用了模块版本库中的一次提交。而与此同时,该提交对象的散列值也是主版本库中每次提交的一个部分。模块版本库中随后的新提交并不会自动被记录在主版本库中。这种操作必须要显式执行,以便我们在主版本库中恢复某一项目版本时可以获取与之相匹配的、模块版本库中的项目版本。

使用子模块中的新版本
在发现子模块中有新版本可用了,我们要怎么做呢?

  1. 更新子模块
    首先,我们需要将子模块的本地工作区调整到理想的状态。通常情况下,我们应该执行一次fetch 命令,以获取模块版本库中的最新提交。
    > cd sub
    > git fetch
    接下来,我们要用checkout 命令指定自己所需要的提交。
    > git checkout v2.0
  2. 使用新版本
    最后,将该新提交预备到模块目录中,并提交它。
    > cd ..
    > git add sub
    > git commit -m "New version of the submodule"

如果我们想在主版本库中使用模块版本库的某一新版本,就必须要对其进行显式修改。如果我们同时在主版本库与模块版本库中工作,就必须要将修改同时提交到两个版本库中。如果你还有一个中央版本库,那么这两个版本库都必须分别执行 push 命令,各自单独完成传送。

与子模块相关的工作
在工作区中,主版本库与模块版本库中的文件都已经被修改了。随后,主版本库应该要指向模块版本库中的新提交。

  1. 提交并推送模块版本库中的修改
    首先,我们要对模块版本库中的修改完成一次提交,并在可能的情况下将其用 push 命令传送给中央版本库。
    > cd sub
    > git add foo.txt
    > git commit -m "Changed submodule"
    > git push
  2. 提交并推送主版本库中的修改
    接下来,我们要将主版本库中的修改,其中包括对模块版本库的引用提交,并在必要 的情况执行传输。
    > cd ..
    > git add bar.txt
    > git add sub
    > git commit -m "New version of submodule"

每次在对包含子模块的工作区执行更新之后后,我们应该随之调用 submodule update 命令来获得各子模块的正确版本。
如果这次是添加了一个全新的子模块,那么在执行submodule update 命令之前,我们还应该先调用一下 submodule init 命令。
另外作为开发者,如果我们在每次更新工作区内容(包括签出、合并、变基、重置、拉取等操作)之后都要执行一次初始化-更新命令序列,就说明事情做得不够好。

更新子模块
如果某子模块的新版本是由别的开发者所记录,那么我们就应该更新自己本地的克隆版本库和工作区。

 > git submodule init> git submodule update
From /project/sub091559e..4722848   master  -> origin/master
★[new tag]  v1.0  -> v1.0
★[new tag]  v2.0  -> v2.0
Submodule path  'sub':
checked out  '472284843ce4c0b0bb503bc4921ab7...le51'

当然,只有在当前工作区中没有相应的模块项的时候, submodule init 命令才会将 .gitmodules 文件中的信息传送给.git/config 文件。这样一来,我们就可以对模块版本库的路径进行本地化调整了。但如果这时有另一个开发者已经修改了.gitmodules 文件中的正式路径, 我们的修改就不会被接受。这就必须要通过 submodule sync 命令来完成此任务了。该命令会更新.git/config 文件中的路径并覆盖掉所有的本地修改。


2️⃣ 与子树之间的依赖

利用子树的概念,我们可以将一些模块版本库嵌入到某一个 Git 版本库中。为了实现这一点,我们必须要将该版本库中的某一目录与模块版本库中的某一提交、标签或分支关联起来。但与子模块不同的是,这回是一个被嵌入的模块版本库,其全部内容是被导入主版本库,而不在仅仅是引用了。这使得主目录中的工作相对更为自给自足了。

下面,我们通过下图来看一下子树处理的基本结构。在该图中,我们有 main 和 sub 两个版本库:我们(通过subtree add命令) 将主目录中的sub 目录与模块目录链接了起来。
而在主版本库的 sub 目录下,我们看到了来自模块版本库中某一版本的文件。

在这里插入图片描述

从技术上来说, subtree add 命令会将模块版本库中所有的提交都导入到主版本库中(即 提交 S1 和 S2) 。然后,主版本库的当前分支就被链接到了模块版本库的特定提交上(即合并提交 G3) 。 在内部, Git 用到了它的子树合并策略(-strategy=subtree )。这样一来就在特定的目录里出现了一次合并,将模块版本库中的内容载入到了sub 目录下。

嵌入一个子树
如果想要嵌入一个模块版本库,我们就要通过 subtree add 命令将它添加到主版本库中(只需要调用一次 subtree add即可)。在这种情况下,你可以通过 -prefix 选项来指定目录。
此外,目标模块库及其标签或分支的URL 也必须要指定。
> git subtree add --prefix=sub /global-path-to/sub v2.0
如果模块版本库的历史记录无需与主版本库相关,你也可以用--squash 选项限制其只 获取特定提交的内容。
> git 'subtree add --squash --prefix=sub /global-path-to/sub master
该命令会产生一个新的合并提交,并会以注释的形式添加它的散列值,这可以使得我们在下次更新时获取正确的模块提交。

与子模块不同的是,当某一带子树的版本库被克隆时,我们通常并不会观察到什么特殊情况。 一般情况下, clone 命令都会去捡取整个主版本库以及它所包含的所有模块版本库。

> git clone /path-to/main

使用子树中的新版本
以下操作的前提是被嵌入的子树中已经有别的版本正在使用。我们可以用subtree pull 命令来更新一个已被嵌入的子树。只要是可用于 subtree add 的 参数都可用于 subtree pull 命令。如果你在使用添加命令时使用了一个标签,必须用一个新的标签来代替。如果已经使用了一个分支,也可以指定是同一分支还是不同分支。如果该 分支上没有任何修改,subtree pull 命令就不会做任何事。
> git subtree pull --prefix=sub /global-path-to/sub v2.1
此外,通过在拉取操作中使用--squash 选项,我们可以跳过模块版本库的历史记录。 在这种情况下,没有中间提交会被涉及到,只有那个被指定的提交。当然,我们也可以用 --squash 选项返回到模块版本库的某一个旧版本上,例如,从2.0版回到1.5版。
> git subtree pull --squash --prefix=sub /global-path-to/sub master

另外通过子树,我们才有可能直接在嵌入式模块的目录中做某些修改。在这里,如果我 们并没有什么特别需求的话。只需调用一般性的commit 命令就可以了。当然,我们也可以将主版本库中的相关修改或者某一提交中一个或多个模块目录版本化。
只有在重发各版本库中对模块所所做的修改时,我们才需要采取一些预防性措施。

扩散模块版本库中的修改
在这里,我们要将在模块目录中所做的修改传送相应的模块版本库中去。

  1. 分离模块目录中的修改
    首先,我们要用 subtree split 命令将模块目录中所发生的修改从其他修改中分离出来。 该命令会基于目前已知模块版本库的提交来生成一个新的提交,该新提交中将包含各提交中那些被修改的了模块文件。该命令执行完后,我们会得到一个指向这个新提交的本地分支(例如 sub/master)。如果你在调用subtree addsubtree pull 命令时没有使用 --squash 选项,在这里可以使用 --rejoin 选项。这可以简化对sqlit 的反复调用。
    > git subtree split --rejoin --prefix sub --branch sub/master
  2. 合并模块版本库中的修改
    模块版本库中的本地修改必须要跟远端的修改进行合并。因此,我们先要激活新建的分支,并检索出目标分支中的最新版本。然后,我们就必须要合并这两个分支。
    > git checkout sub/master
    > git fetch /global-path-to/sub master
    > git merge FETCH_HEAD
    请注意,上面带URL的那个获取操作会创建一个临时引用 FETCH_HEAD, 该引用会指向其获取分支中的最新提交。如果你此刻正在某个远程分支上工作,理所当然可以使用其远程名称而不是URL。在这之后,目标分支将就可直接使用了,并不非得是 FETCH_HEAD。
  3. 将修改传送到模块版本库中,并删除临时分支
    临时分支中的本地修改必须要被推送到远程模块版本库中。在推送完成之后,我们可以切换回主版本库的分支,并删除该临时分支。
    > git push /global-path-to/sub HEAD:master
    > git checkout master
    > git branch -d sub/master

从上述内容,我们可以清楚地看到,大部分子树操作都要比那些相应的子模块简单一些,两者只有在提取修改方面的复杂度是差不多的。
但在多数情况下,我们是不会用到提取操作的,因为我们是在主版本库上工作,而不是 模块目录中。

🌾 总结

  • 嵌入子模块:我们可以通过submodule addsubmodule init 命令来嵌入一个子模块。
  • 克隆包含子模块的项目:我们可以在克隆该项目后,对其调用 submodule initsubmodule update 命令。
  • 选择子模块中的某个新版本:首先,我们要(通过 checkout 命令来看) 选择在子模块目录中的新提交。然后,在主版本库中对其做一次提交。
  • 同时处理模块版本库与主版本库:我们必须要先在模块版本库中执行提交,然后才能在主版本库中执行提交。另外,两个版本库的推送操作也必须要各自执行 push 命令。
  • 嵌入子树:我们可以通过subtree add 命令来嵌入子树。
  • 选择子树中的某个新版本:我们可以通过subtree pull命令来将模块目录更新到所需的分支或标签上。
  • 提取模块目录中的修改:我们可以通过subtree split 命令创建一个单独的分支,用于 包含模块目录在的修改。然后再使用 merge 命令将这些修改与其他修改合并,并用 push 命令完成推送操作。


温习回顾上一篇(点击跳转)
《【Git教程】(九)版本标签 —— 创建、查看标签,标签的散列值,将标签添加到日志输出中,判断标签是否包含特定的提交 ~》

继续阅读下一篇(点击跳转)
《【Git教程】(十一)一些技巧 —— 引用日志、忽略临时性的本地修改、检查对文本文件的修改、Git 命令别名、为临时指向的提交创建分支、将提交移动到另一分支 ~》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/311678.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

雪亮工程视频联网综合管理/视频智能分析系统建设方案(一)

一、行业背景 雪亮工程主要是针对农村地区治安防控的监控项目,在乡村的主干道、路口、人群聚集地部署高清摄像头,通过三级综治中心和指挥平台,将视频图像信息系统纵向下延至县、乡、村,同时利用系统拓展在安防、社会治理、智慧交…

基于U-Net的图像分割算法介绍

U-Net是一种用于图像分割的深度学习架构,其设计初衷是用于生物医学图像分割,尤其是医学影像中的细胞分割任务。U-Net结构独特,具有编码器-解码器结构,能够有效地捕捉图像中的局部和全局信息,并在像素级别上进行精确的分割。 相关论文: U-Net: Convolutional Networks for…

记录一下我hive连不上DataGrip的问题

用户名和密码都没问题,但报如下这个错误 原因:是因为我在linux上没启hiveserver2服务 解决: [atguiguhadoop102 hadoop]$ hiveserver2 which: no hbase in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/module/jdk1.8…

Spring Boot 统一功能处理(二)

本篇主要介绍Spring Boot统一功能处理中的统一数据返回格式。 目录 一、定义统一的返回类 二、配置统一数据格式 三、测试配置效果 四、统一格式返回的优点 五、源码角度解析String问题 一、定义统一的返回类 在我们的接口在处理请求时,返回的结果可以说是参…

Linux-进程控制

目录 1.进程创建 2. 进程终止 2.1 进程退出的场景 2.2 进程常见退出方法 2.3 return返回终止 2.4 exit()和_exit() 3. 进程等待 3.1 进程等待的原因 3.2 wait​编辑 3.3 waitpid 3.4 status 4. 进程替换 4.1 替换原理 4.2 exec函数系列 1.进程创建 在linux中for…

【学习笔记】rt-thread

任务 创建好任务,不管是动态还是静态创建,任务的状态是init ,通过start方法来启动任务;线程大小 设置小了,无法正常工作?显示占空间100% 启动过程 TODO 这是编译器特性? 因为RT-Thread使用编…

代码随想录-算法训练营day14【二叉树01:理论基础、递归遍历、迭代遍历、统一迭代】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第六章 二叉树part01今日内容: ● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代详细布置 理论基础 需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义 文章讲解&#x…

ARM_day7:实现三个按键中断

程序代码: mykey.h: #ifndef __MYKEY_H__ #define __MYKEY_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h" extern void printf(const char …

03_信号和槽

信号和槽 系统的信号和槽自定义信号和槽Lambda表达式 系统的信号和槽 下面我们完成一个小功能,上面我们已经学习了按钮的创建,但是还没有体现出按钮的功能,按钮最大的功能也就是点击后触发一些事情,比如我们点击按钮,…

FAGLL03H 新增自定义字段

1、SGLPOS_N_GL_CT、SGLPOS_N_CT两个结构新增自定义字段 2、执行t-code:HDBVIEWS 3、实施增强 FAGL_LIB 4、使用select data方法 5、代码示例: method IF_FAGL_LIB~SELECT_DATA.FIELD-SYMBOLS: <fs> TYPE any.FIELD-SYMBOLS <ls_data> TYPE any.F…

ctf.show_web13

上传一句话木马 1.php文件&#xff0c;显示 再改后缀为.jpg&#xff0c;显示错误文件大小 用dirsearch扫一下 备份文件.bak 下载文件源码 <?php header("content-type:text/html;charsetutf-8");$filename $_FILES[file][name];$temp_name $_FILES[file][tm…

腾讯清华联合提出图像到视频生成方法-Follow-Your-Click:点击图像并加上简单提示词就可让图像动起来!

Follow-Your-Click只需单击一次和简短的提示就可以让图像的某一部分动起来&#xff0c;还支持不同的动作表达&#xff0c;比如微笑&#xff0c;悲伤&#xff0c;跳舞…… 相关链接 论文链接&#xff1a;https://arxiv.org/abs/2403.08268 项目链接&#xff1a;https://github…

html 引入vue Element ui 的方式

第一种&#xff1a;使用CDN的方式引入 <!--引入 element-ui 的样式&#xff0c;--> <link rel"stylesheet" href"https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <!-- 必须先引入vue&#xff0c; 后使用element-ui --> <…

基于Docker构建CI/CD工具链(六)使用Apifox进行自动化测试

添加测试接口 在Spring Boot Demo项目里实现一个简单的用户管理系统的后端功能。具体需求如下&#xff1a; 实现了一个RESTful API&#xff0c;提供了以下两个接口 &#xff1a; POST请求 /users&#xff1a;用于创建新的用户。GET请求 /users&#xff1a;用于获取所有用户的列…

凡泰极客亮相2024 亚马逊云科技出海全球化论坛,为企业数字化出海赋能

随着「不出海&#xff0c;即出局」登上热搜榜单&#xff0c;企业出海已成燎原之势&#xff0c;3月29日&#xff0c;2024 亚马逊云科技出海全球化论坛在深圳成功举办&#xff0c;凡泰极客创始人梁启鸿受邀出席&#xff0c;并以 「App 2.0&#xff1a;以SuperApp构建智能数字生态…

HADOOP大数据处理技术8-JavaSe

投入地跳舞 就像没有人在一旁看着你一样 2024/4/8 5&#xff09;方法重载&#xff1a;在方法中 方法名称相同 但参数列表不同 方法名称相同 但是参数类型或个数不一样 好处&#xff1a;好记 6&#xff09;super &#xff08;只在具有继承关系的子类中使用&#xff09; 作用&a…

JAVA基础07-封装,类加载原理以及分析(内有练习代码)

目录 封装的理解 概念 目的 权限修饰符 访问权限从大到小 如何快速定义一个标准的Java类 1.普通方法 2.快捷键 练习 static 定义 静态与非静态区分 使用静态与非静态的场合 成员变量和局部变量 成员变量 局部变量 例子讲解&#xff1a;两数交换 解决方法 变…

研发岗-统信UOS系统配置npm git等前端常用配置

第一步 获取root权限 配置环境等都需要用到root权限&#xff0c;所以我们先获取到root权限&#xff0c;方便下面的操作 下载软件 在UOS应用商店下载的所需应用 版本都比较低 安装node 官网下载了【arm64】的包&#xff0c;解压到指定文件夹&#xff0c;设置链接&#xff0…

如何降低漏测, 避免上线后出bug,6年测试心得分享

一、漏测原因总结 &#xff08;1&#xff09;需求评审质量低&#xff0c;需求设计简单、只是简单描述功能&#xff0c;功能逻辑较少   &#xff08;2&#xff09;需求变更频繁   &#xff08;3&#xff09;缺少需求分解&#xff08;sql 文档、用例设计&#xff09;   &…

Unity 左右折叠显示与隐藏UI的简单实现

要实现一个简单的UI左右折叠显示与隐藏&#xff0c;可以结合遮罩&#xff0c;通过代码控制UI区块的宽度和位移来实现。 具体可以按以下步骤实现&#xff1a; 1、新建一个Image组件&#xff0c;并添加精灵&#xff0c;调整大小后&#xff0c;复制一份作为该UI的父物体&#xf…