Git中submodule的使用

2023-05-16

背景

面对比较复杂的项目,我们有可能会将代码根据功能拆解成不同的子模块。主项目对子模块有依赖关系,却又并不关心子模块的内部开发流程细节。

这种情况下,通常不会把所有源码都放在同一个 Git 仓库中。

有一种比较简单的方式,是在当前工作目录下,将子模块文件夹加入到 .gitignore 文件内容中,这样主项目就能够无视子项目的存在。这样做有一个弊端就是,使用主项目的人需要有一个先验知识:需要在当前目录下放置一份某版本的子模块代码。

还有另外一种方式可供借鉴,可以使用 Git 的 submodule 功能,也是这篇文章的主题。

实际上 Git 工具的 submodule 功能就是建立了当前项目与子模块之间的依赖关系:子模块路径子模块的远程仓库子模块的版本号

使用流程

假定我们有两个项目:project-main 和 project-sub-1,其中 project-main 表示主项目,而 project-sub-1 表示子模块项目。

其中 project-main 的远程仓库地址为 https://github.com/username/project-main.git,而 project-sub-1 的远程仓库地址为 https://github.com/username/project-sub-1.git

接下来,我们希望在 project-main 中添加 project-sub-1 ,而又保持 project-sub-1 自身独立的版本控制。

1.创建 submodule

使用 git submodule add <submodule_url> 命令可以在项目中创建一个子模块。

进入项目 project-main ,输入:

➜ project-main git:(master) git submodule add  https://github.com/username/project-sub-1.git
正克隆到 '/path/to/project-main/project-sub-1'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
展开对象中: 100% (3/3), 完成.

此时项目仓库中会多出两个文件:.gitmodules 和 project-sub-1 。

前者的内容是这样的,事实上就是子模块的相关信息;而后者那个文件,实际上保存的是子模块当前版本的版本号信息。

[submodule "project-sub-1"]
path = project-sub-1
url =  https://github.com/username/project-sub-1.git

如果此前项目中已经存在 .gitmodules 文件,则会在文件内容中多出上述三行记录。

事实上,此时在 .git/config 文件中也会多出一些信息,在 .git/modules 文件夹下也会多出一份内容。

通常此时可以使用 git commit -m "add submodule xxx" 提交一次,表示引入了某个子模块。提交后,在主项目仓库中,会显示出子模块文件夹,并带上其所在仓库的版本号。

2.获取 submodule

上述步骤在创建子模块的过程中,会自动将相关代码克隆到对应路径,但对于后续使用者而言,对于主项目使用普通的 clone 操作并不会拉取到子模块中的实际代码。

使用以下命令进行克隆,完成后 project-main/project-sub-1 文件夹是空的:

cd /path/to/temp
git clone  https://github.com/username/project-main.git

如果希望子模块代码也获取到,一种方式是在克隆主项目的时候带上参数 --recurse-submodules,这样会递归地将项目中所有子模块的代码拉取。

cd /path/to/temp2
git clone  https://github.com/username/project-main.git --recurse-submodules

此时 project-main/project-sub-1 文件夹是有内容的,并且固定在某个 Git 提交的版本上。

另外一种可行的方式是,在当前主项目中执行:

git submodule init
git submodule update

则会根据主项目的配置信息,拉取更新子模块中的代码。

3.子模块内容的更新

对于子模块而言,并不需要知道引用自己的主项目的存在。对于自身来讲,子模块就是一个完整的 Git 仓库,按照正常的 Git 代码管理规范操作即可。

对于主项目而言,子模块的内容发生变动时,通常有三种情况:

1)当前项目下子模块文件夹内的内容发生了未跟踪的内容变动;

2)当前项目下子模块文件夹内的内容发生了版本变化;

3)当前项目下子模块文件夹内的内容没变,远程有更新;

> 情况1:子模块有未跟踪的内容变动

对于第1种情况,通常是在开发环境中,直接修改子模块文件夹中的代码导致的。

此时在主项目中使用 git status 能够看到关于子模块尚未暂存以备提交的变更,但是于主项目而言是无能为力的,使用 git add/commit 对其也不会产生影响。

➜ project-main git:(master) git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
(提交或丢弃子模组中未跟踪或修改的内容)
修改: project-sub-1 (未跟踪的内容)
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")

在此情景下,通常需要进入子模块文件夹,按照子模块内部的版本控制体系提交代码。

当提交完成后,主项目的状态则进入了情况2,即当前项目下子模块文件夹内的内容发生了版本变化。

> 情况2:子模块有版本变化

当子模块版本变化时,在主项目中使用 git status 查看仓库状态时,会显示子模块有新的提交:

➜ project-main git:(master) ✗ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: project-sub-1 (新提交)
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")

在这种情况下,可以使用 git add/commit 将其添加到主项目的代码提交中,实际的改动就是那个子模块 文件 所表示的版本信息:

git diff HEAD HEAD^
diff --git a/project-sub-1 b/project-sub-1
index ace9770..7097c48 160000
--- a/project-sub-1
+++ b/project-sub-1
@@ -1 +1 @@
-Subproject commit ace977071f94f4f88935f9bb9a33ac0f8b4ba935
+Subproject commit 7097c4887798b71cee360e99815f7dbd1aa17eb4

通常当子项目更新后,主项目修改其所依赖的版本时,会产生类似这种情景的 commit 提交信息。

> 情况3:子模块远程有更新

通常来讲,主项目与子模块的开发不会恰好是同时进行的。通常是子模块负责维护自己的版本升级后,推送到远程仓库,并告知主项目可以更新对子模块的版本依赖。

在这种情况下,主项目是比较茫然的。

之前曾经提到,主项目可以使用 git submodule update 更新子模块的代码,但那是指 当前主项目文件夹下的子模块目录内容 与 当前主项目记录的子模块版本 不一致时,会参考后者进行更新。

但如今这种情况下,后者 当前主项目记录的子模块版本 还没有变化,在主项目看来当前情况一切正常。

此时,需要让主项目主动进入子模块拉取新版代码,进行升级操作。

通常流程是:

cd project-sub-1
git pull origin master

子模块目录下的代码版本会发生变化,转到情况2的流程进行主项目的提交。

当主项目的子项目特别多时,可能会不太方便,此时可以使用 git submodule 的一个命令 foreach 执行:

git submodule foreach 'git pull origin master'

> 情况汇总

终上所述,可知在不同场景下子模块的更新方式如下:

  • 对于子模块,只需要管理好自己的版本,并推送到远程分支即可;
  • 对于父模块,若子模块版本信息未提交,需要更新子模块目录下的代码,并执行 commit 操作提交子模块版本信息;
  • 对于父模块,若子模块版本信息已提交,需要使用 git submodule update ,Git 会自动根据子模块版本信息更新所有子模块目录的相关代码。

4.删除子模块

网上流传了一些偏法,主要步骤是直接移除模块,并手动修改 .gitmodules.git/config 和 .git/modules 内容。包含了一大堆类似git rm --cached <sub-module>rm -rf <sub-moduel>rm .gitmodules 和 git rm --cached 之类的代码。

实际上这是一种比较野的做法,不建议使用。

根据官方文档的说明,应该使用 git submodule deinit 命令卸载一个子模块。这个命令如果添加上参数 --force,则子模块工作区内即使有本地的修改,也会被移除。

git submodule deinit project-sub-1
git rm project-sub-1

执行 git submodule deinit project-sub-1 命令的实际效果,是自动在 .git/config 中删除了以下内容:

[submodule "project-sub-1"]
url =  https://github.com/username/project-sub-1.git

执行 git rm project-sub-1 的效果,是移除了 project-sub-1 文件夹,并自动在 .gitmodules 中删除了以下内容:

[submodule "project-sub-1"]
path = project-sub-1
url =  https://github.com/username/project-sub-1.git

此时,主项目中关于子模块的信息基本已经删除(虽然貌似 .git/modules 目录下还有残余):

➜ project-main git:(master) ✗ gs
位于分支 master
您的分支与上游分支 'origin/master' 一致。
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
修改: .gitmodules
删除: project-sub-1

可以提交代码:

git commit -m "delete submodule project-sub-1"

至此完成对子模块的删除。

总结

当项目比较复杂,部分代码希望独立为子模块进行版本控制时,可以使用 git submodule 功能。

使用 git submodule 功能时,主项目仓库并不会包含子模块的文件,只会保留一份子模块的配置信息及版本信息,作为主项目版本管理的一部分。

本篇文章简单介绍了 git submodule 的添加和删除,以及项目开发过程中主项目与子模块不同状态时刻的操作方式。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Git中submodule的使用 的相关文章

  • 仅使用 Git grep 的文件名

    我只想查看文本中包含特定单词的不同文件 current directory git grep word 显示文件中具有匹配单词的每一行 所以我尝试了这个 current directory git grep word files with
  • `git push` -- 没有输出,什么也没有发生

    touch test git add test git commit m test git push u origin master 这奏效了 该文件已上传到存储库 rm test cp R website website git rm t
  • Git:从 master 以外的分支克隆

    我正在尝试从 Github 的存储库中提取数据 但我不想克隆主分支 我想克隆其他一些分支 当我尝试时git clone
  • 显示 master 之前/之后有多少提交分支的别名

    新的 Bitbucket Branches 页面非常棒 它显示每个分支领先 落后于 master 的提交数量 是否有显示相同信息的 Git 别名 信息应显示 分店名称 上次更新是什么时候 其背后有多少提交 有多少提交领先于 master 看
  • 将更改从一个分支复制到另一个分支

    我有一个分支名为BranchA from master 我有一些改变BranchA 我不会合并来自BranchA to master 现在我创建了另一个分支master named BranchB 我如何复制更改BranchA to Bra
  • git merge 冲突的不同场景

    我试图了解 git 合并后可能发生 git 冲突的情况以及如何避免它们 我创建了一个 git 存储库并向其中添加了一个文本文件 我已将 1 添加到文本文件中并将其提交给 master 我已经从 master 创建了一个新分支 分支 2 并将
  • 有没有一个简单的命令可以将分支转换为标签?

    我即将完成将 哑快照 转换为 git 的繁琐过程 这个过程进展得非常顺利 感谢这个重命名过程 https stackoverflow com questions 6628539 how to tell git that its the sa
  • 删除 Git 存储库,但保留所有文件

    在我使用 Linux 的过程中的某个时刻 我决定将我的主目录中的所有内容都放入源代码管理中是个好主意 我不是在问这是否是一个好主意 我是在问如何撤销它 删除存储库的原因是我最近安装了 Oh My Zsh 而且我非常喜欢它 问题是我的主目录有
  • 如何诊断和修复 git fatal: 无法读取树

    我在用着git管理项目上的文件 并不断遇到这个问题 当我跑步时git status我收到消息 fatal unable to read tree e2d920161d41631066945a3cbcd1b043de919570 据我了解 我
  • 哪些 git hooks 适用于“git rebase --continue”?

    我正在尝试为我的组织构建一组 git hook 脚本 我想使用的一个脚本 仅用于我自己的多个项目 将是检查git rebase continue我的代码中没有留下任何冲突标记 lt lt lt lt lt or gt gt gt gt gt
  • 如何更改 GitHub 上的文件模式?

    git add test file git commit m first commit create mode 100644 test file git push git update index add chmod x test file
  • 如何仅根据拉取请求在 Jenkins 中运行阶段?

    我现在有一个基于 Jenkinsfile 的管道 其中包含多个阶段 每次提交到 Github 时都会由 webhook 触发 我想在每次提交时保持 构建 和 单元测试 阶段运行 但仅在分支准备拉取请求时运行 集成测试 阶段 我想要的是 st
  • 使用 git 子树时如何添加特定文件夹?

    我正在开发一个复杂的 Ionic 项目 我正在开发的许多组件和提供程序都是通用的 可以在我公司正在进行的其他项目中使用 这在软件开发中很常见 这是我提出的 Git 工作流程 该图显示了分支 my company library repo c
  • 在git的远程存储库上创建私有分支

    我想在我们公司的 git 上构建特定的流程 开发人员在他的本地计算机上创建一个分支并在那里提交一些文件 dev 将此分支推送到远程仓库 其他开发者无法访问该分支 经过几轮推动开发人员决定发布他的更改 将他的私人分支合并到公共分支 推动该公共
  • 如何从 Git 存储库中删除选定的提交日志条目,同时保留其更改?

    我想从线性提交树中删除选定的提交日志条目 以便这些条目不会显示在提交日志中 我的提交树看起来像 R A B C D E HEAD 我想删除 B 和 C 条目 以便它们不会显示在提交日志中 但应保留从 A 到 D 的更改 也许通过引入单个提交
  • 无法在 Windows 10 上运行 Python 3.7“权限被拒绝”

    当尝试使用 Git Bash 在 Windows 10 上运行 Python 3 7 时 出现以下错误 python version bash c Users Name AppData Local Microsoft WindowsApps
  • Git hook:如果创建了新分支,则将新文件添加到存储库

    我正在编写一个 git hook 它检查是否创建了新分支 如果是 则将一些预定义文件添加到该新分支的存储库中 一些配置文件 然而 由于分支实际上正在创建过程中 所以我的逻辑失败了 目前我正在这样做post receive钩子 看起来像这样
  • 如何将yarn add/npm install与monorepos一起使用

    我需要从 GitHub 中的私有 monorepo 下载节点包 类似于 monorepoProject subProjectA subProjectB 还有两个子项目A and 子项目B是 typescript 项目 如下图所示 subPr
  • 使用 ssh-keygen 创建 SSH 密钥不会创建 .ssh 文件夹

    我正在尝试使用 msysgit 创建我的公共 私有 rsa 密钥对 我运行这个命令 ssh keygen C email protected cdn cgi l email protection t rsa 一切看起来都很好 我收到消息了
  • “submodule”似乎是一个 git 命令,但我们无法执行它

    我已经克隆了一个 git 存储库 它是一个 Angular 7 和 NET Core 应用程序 项目中一切正常 但是当我尝试恢复 npm 包时 出现以下错误 Microsoft TeamFoundation Team Explorer Gi

随机推荐

  • 安装APK时报错:Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]

    使用AS自动运行时会在app build outputs apk debug文件夹下自动生成测试APK xff1a app debug apk xff0c 用命令adb install app debug apk时报错 xff1a Fail
  • 计算机网络-划分子网 四大类必会题型

    必记知识点 A类 xff1a 0 126 xff0c 默认子网掩码 xff1a 255 0 0 0 B类 xff1a 128 191 xff0c 默认子网掩码 xff1a 255 255 0 0 C类 xff1a 192 223 xff0c
  • C语言-解释复杂声明

    基本术语 xff1a 声明符 xff1a int a 就是一个声明符 标识符 定义的变量名字 xff0c 如 xff1a int a xff0c 那么a就是一个标识符 1 两个原则 xff1a 始终从内往外读声明符 xff0c 括号优先级高
  • Android EditText 不自动获取焦点

    在Activity上面显示一个EditText xff0c 进入该页面时想阻止这个EditText自动获取焦点而自动调起键盘 思路如下 xff1a 可以采取让父级控件来获取焦点就可以了 例如说在这个EditText外面包一个LinearLa
  • Wireshark中无法显示网卡列表的解决方法

    1 问题描述 打开Wireshark时 xff0c 都会有一个网卡列表 xff0c 在该列表中显示了电脑的所有网卡 但是 xff0c 有时打开Wireshark时 xff0c 该网卡列表不显示 xff0c 如图1所示 图1 不显示网卡列表
  • Android事件分发与事件处理源码分析

    一 前言 Android中事件分发与事件处理是一个老生常谈的问题了 xff0c 自己在网上也看过很多文章 xff0c 但是大部分人都只是抛出一些结论或是一些流程图或者干脆就是一些运行demo的截图等 xff0c 对于这些结论和流程图是怎么来
  • 解决小米pad USB安装apk时AS报错:INSTALL_FAILED_USER_RESTRICTED

    设置 更多设置 开发者选项 gt 取消启用MIUI优化 用USB接口 xff0c 选择传输文件 xff08 MTP xff09
  • git 通过 comment 关键字查找 commit

    git log grep 61 word 比如 xff1a git log grep 61 同步
  • git合并多个 Commit

    在使用 Git 作为版本控制的时候 xff0c 我们可能会由于各种各样的原因提交了许多临时的 commit xff0c 而这些 commit 拼接起来才是完整的任务 那么我们为了避免太多的 commit 而造成版本控制的混乱 xff0c 通
  • git查看某次提交的文件列表

    Git操作常用 xff1a 一 查看某次提交的文件列表 首先使用git log查看历史提交记录 xff1a 复制你想要查看记录的某个提交代号9ddc9dca00b 使用命令git show 9ddc9dca00b stat查看详细文件列表
  • Android SoundPool插入耳机后依然有外放声音

    使用soundPool播放声音 xff0c 当手机已经接通耳机时 xff0c 还会有外放声音 xff0c 是因为在初始化soundpool是用的流类型 xff08 streamType xff09 导致的 xff0c 有些流类型系统是一定会
  • 一文搞懂Android JetPack组件原理之Lifecycle、LiveData、ViewModel与源码分析技巧

    Lifecycle LiveData和ViewModel作为AAC架构的核心 xff0c 常常被用在Android业务架构中 在京东商城Android应用中 xff0c 为了事件传递等个性化需求 xff0c 比如ViewModel间通信 V
  • Linux下安装npm

    1 root 登录linux 2 没有目录就自己创建一个 cd usr local node 3 下载安装包 wget https npm taobao org mirrors node v4 4 7 node v4 4 7 linux x
  • AudioService之音频输出通道切换

    前言 xff1a 音频输出的方式有很多种 xff0c 外放即扬声器 xff08 Speaker xff09 听筒 xff08 Telephone Receiver xff09 有线耳机 xff08 WiredHeadset xff09 蓝牙
  • Android音频——音量调节

    一 音量相关概念 1 相关术语解释 track volume 单个App设置音量时设置的是这个 xff0c 它只影响本App的音量 stream volume xff1a 设置某一stream的音量 xff0c Android系统中支持10
  • 电话状态权限及IMEI获取流程源码分析

    IMEI是设备唯一性的一个重要指标 xff0c 这篇文章对IMEI获取做一些分析 xff0c 以达到以下两个目的 xff1a 1 梳理Android源码中获取IMEI流程 2 理解获取IMEI时 xff0c 源码中权限调用流程 备注 xff
  • Android Handler深入学习(源码分析)

    目录 xff1a 1 背景 在分析源码之前 xff0c 先来了解一下Message MessageQueue Looper这几个对象 1 1 Message 消息 定义 xff1a 是线程间通讯的数据单元 xff0c 包含着描述信息及任意数
  • git 合并两个不同仓库

    在日常开发过程中 xff0c 可能会遇到需要将两个不同的仓库合并成到一个仓库的场景 这里介绍一下怎么将两个不同的仓库合并到一个仓库中 合并两个不同仓库 思路 xff1a 添加两个远程仓库 xff0c 将两个代码作为两个分支 xff0c 然后
  • Android-Handler源码解析-Message

    成员变量 标识Message public int what 存储简单数据 xff0c 如果存储复杂的数据使用setData 方法 public int arg1 public int arg2 发送给接收者的任意对象 public Obj
  • Git中submodule的使用

    背景 面对比较复杂的项目 xff0c 我们有可能会将代码根据功能拆解成不同的子模块 主项目对子模块有依赖关系 xff0c 却又并不关心子模块的内部开发流程细节 这种情况下 xff0c 通常不会把所有源码都放在同一个 Git 仓库中 有一种比