【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/sub
	091559e..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 命令别名、为临时指向的提交创建分支、将提交移动到另一分支 ~》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

修复开始菜单消失或不能工作的几种方法,总有一种适合你

如果Windows开始菜单消失或按Windows键时无法打开,请修复Windows 11或Windows 10 PC上的一些系统组件,使菜单重新工作。下面是如何做到这一点。 作为基本修复,请重新启动Windows 11或Windows 10 PC,看看是否解决了问题。如果没有,请使用以下故障排除方法。 使任务栏可见…

MATLAB如何分析根轨迹(rlocus)

根轨迹分析是一种图形化方法,用于研究闭环极点随系统参数(通常是反馈增益)变化时的移动情况。 绘制根轨迹目的就是改变系统的闭环极点,使得系统由不稳定变为稳定或者使得稳定的系统变得更加稳定。 主导极点 主导极点就是离虚轴最近的闭环极…

【通信原理笔记】【三】——3.7 频分复用

文章目录 前言一、时分复用(TDM)二、频分复用(FDM)总结 前言 现在我们学习了几种调制模拟基带信号的方法,这些调制方法可以将基带信号搬移到频带进行传输。那么如果采用不同的载波频率把多个基带信号搬移到不同的频带…

京东详情比价接口优惠券(2)

京东详情API接口在电子商务中的应用与作用性体现在多个方面,对于电商平台、商家以及用户都带来了显著的价值。 首先,从应用的角度来看,京东详情API接口为开发者提供了一整套丰富的功能和工具,使他们能够轻松地与京东平台进行交互。…

从数据中台到上层应用全景架构示例

一、前言 对于大型企业而言,数据已经成为基本的生产资料,但是有很多公司还是值关心上层应用,而忽略了数据的治理,从而并不能很好的发挥公司的数据资产效益。比如博主自己是做后端的,主要是做应用层,也就是…

【研发效能·创享大会-嗨享技术轰趴】-IDCF五周年专场

一、这是一场创新分享局! 来吧,朋友们! 参加一场包含AIGC、BizDevOps、ToB产品管理、B端产品运营、平台工程、研发效能、研发度量、职业画布、DevOps国标解读的研发效能创享大会,会有哪些收益呢? 知识更新与技能提升:…

2024妈妈杯mathorcup数学建模C题 物流网络分拣中心货量预测及人员排班

一、数据预处理 数据清洗是指对数据进行清洗和整理,包括删除无效数据、缺失值填充、异常值检测和处理等。数据转换是指对数据进行转换和变换,包括数据缩放、数据归一化、数据标准化等。数据整理是指对数据进行整理和归纳,包括数据分组、数据聚…

记一次http访问超时服务器端调试

问题:http访问服务器时没有返回,没有超时,一直在阻塞 处理过程:telnet端口能连上,服务端程序也不存在处理时间过长的情况。 说明tcp连接没问题。推测是客户端连接后再发起请求,服务端阻塞了。因为很多客户…

2024-4-12-实战:商城首页(下)

个人主页:学习前端的小z 个人专栏:HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 作业小结 作业 .bg-backward {width: 60px; height: 60px;background: url(..…

Java集合(一)Map(1)

Map HashMap和HashTable区别 线程是否安全:HashMap线程不安全,HashTable线程安全。因为HashTable内部的方法都经过了synchronized关键字修饰。 HashMap线程不安全例子:如果两个线程都要往HashMap中插入数据,但是发生哈希冲突&…

【爬虫+数据清洗+可视化分析】python文本挖掘“狂飙“的哔哩哔哩评论

一、背景介绍 2023年《狂飙》这部热播剧引发全民追剧,不仅全员演技在线,更是符合反黑主旋律,因此创下多个收视率记录! 基于此热门事件,我用python抓取了B站上千条评论,并进行可视化舆情分析。 二、爬虫代…

Aconda教程

1.创建Aconda的虚拟环境 conda create -n 虚拟环境名字2.查看Conda有哪些虚拟环境 conda env list3.激活Conda的虚拟环境 conda activate 虚拟环境名4.查看conda的镜像源 conda config --show 5.conda安装cpu版本的pytorch pip3 install torch torchvision torchaudio 6.…

YOLOv8绝缘子边缘破损检测系统(可以从图片、视频和摄像头三种方式检测)

可检测图片和视频当中出现的绝缘子和绝缘子边缘是否出现破损,以及自动开启摄像头,进行绝缘子检测。基于最新的YOLO-v8训练的绝缘子检测模型和完整的python代码以及绝缘子的训练数据,下载后即可运行。(效果视频:YOLOv8绝…

【机器学习】Logistic与Softmax回归详解

在深入探讨机器学习的核心概念之前,我们首先需要理解机器学习在当今世界的作用。机器学习,作为人工智能的一个重要分支,已经渗透到我们生活的方方面面,从智能推荐系统到自动驾驶汽车,再到医学影像的分析。它能够从大量…

【linux深入剖析】动态库的使用(续) | 动静态库的链接

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 回顾1. 打包库的使用2. 动…

JavaWeb--JavaScript-事件绑定/BOM/DOM编程

目录 1. 事件绑定 1.1. 什么是事件 1.2. 常见事件 1.3. 事件的绑定 1.3.1. 属性绑定 1.3.2. DOM编程绑定 1.4. 事件的触发 1.4.1. 行为触发 1.4.2. DOM编程触发 2. BOM 编程 2.1. 什么是 BOM 2.2. window对象的常见属性(了解) 2.3. window对象的常见方法(了解) 2…

如何准备2024年汉字小达人:18道历年考题示例和解析、备考提醒

现在距离2024年第11届汉字小达人比赛还有六个多月的时间,如何利用这段时间有条不紊地备考呢?我的建议是两手准备:①把小学1-5年级的语文课本上的知识点熟悉,重点是字、词、成语、古诗。阅读理解不需要。②把历年真题刷刷熟&#x…

nacos服务器挂了之后springboot/springcloud服务会挂吗?不会挂(顺便深入源码分析nacos配置中心客户端核心功能实现)

文章目录 nacos挂了之后服务会挂吗?不会挂(深入源码分析)展开nacos客户端源码找本地缓存配置相关文件客户端内存缓存客户端健康状态获取配置的实现 nacos挂了之后服务会挂吗?不会挂(深入源码分析) 展开nac…

适用于数据找回恢复的 12 个免费数据恢复工具

技术使我们的生活一天比一天轻松,我们已经越来越习惯于使用电脑、智能手机、桌子等设备,我们喜欢使用手机、电脑和其他数字设备,并将我们宝贵的数据存储在它们上面。当然,我们不能忍受丢失数据,因为这些设备都不可靠。…

C语言如何生成随机数以及设置随机数的范围

一、随机数的生成 1.rand()函数 C语言提供了⼀个函数叫 rand,这函数是可以生成随机数的,函数原型如下所示: int rand (void); rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的大小是依…