0. 简介

在面对复杂系统时,所有的模块不可能同时开发在一个project下的,而更多的可能就是每个人开发不同的模块,并通过一个模块将这些模块都整合到一起,这时候submodule的作用就非常明显了。通过设置submodule可以轻易地对不同的模块完成整合。同时大部分现代软件项目都需要依赖于他人的工作,当别人已经实现了一个很好的解决方案,就不需要再浪费时间再去实现一遍。因此很多项目都会以库或模块的形式使用第三方代码。本文将对submodule进行详细的说明,并向大家展示如何使用submodule。

1. submodule定义

为了弄清楚为什么Git的submodule很有价值,让我们先看一个没有submodule的例子。当我们需要引用第三方代码(比如开源库)的时候,最简单的办法是从GitHub下载代码,然后将其保存到自己的项目中。虽然这么做可以很快解决问题,但这么做非常丑陋,原因如下:

  • 通过强制将第三方代码复制到项目中,我们可以有效的将多个项目混合到一个项目中,而我们自己的项目和别人的项目(库)之间的界限开始变得模糊。
  • 每当我们需要更新库代码时(也许是因为它的维护者提供了一个很棒的新特性或修复了一个讨厌的bug),我们都必须再次下载、复制和粘贴,这很快就变成了一个乏味的过程。

软件开发中“将不同的东西分开”的一般规则是有原因的。当然,在自己的项目中管理第三方代码也需要这样,而Git submodule的概念正是针对这些情况而设计的。

2. submodule操作

2.1 创建子模块

举一个典型的例子,假设我们想要向项目中添加一个第三方库,在我们获取任何代码之前,需要先创建一个单独的文件夹,作为第三方库存储的路径

mkdir -p SLAM/2d-slam
cd SLAM/2d-slam

现在,我们准备将一些第三方代码以submodule的方式注入到项目中

# 使用 ssh完成 submodule
#git submodule add git@github.com:Lcp1/cartographer-note.git
# 默认url路径更新
# git submodule add https://github.com/Lcp1/cartographer-note.git
# 对submodule完成重命名
git submodule add --name cartographer-note https://github.com/Lcp1/cartographer-note.git

当我们运行这个命令时,Git开始将repository作为submodule克隆到我们的项目中:
在这里插入图片描述

然后我们就可以看到在对应的路径下已经加到项目里了。与此同时在主项目的根文件夹中创建了一个新的.gitmodules文件。

[submodule "2d-slam/cartographer-note"]
    path = 2d-slam/cartographer-note
    url = https://github.com/Lcp1/cartographer-note.git

.gitmodules文件是Git用来跟踪项目中的submodule的几个配置之一,另一个是.git/config,它的结尾被添加了下面的配置:

[submodule "2d-slam/cartographer-note"]
    active = true
    url = https://github.com/Lcp1/cartographer-note.git

最后,Git还在内部的.git/modules文件夹中保存了每个子模块的.git仓库的副本。当然你不需要记住所有这些技术细节。可以看到,Git submodule的内部维护是相当复杂的,因此请记住:千万不要手工修改Git子模块的配置!如果你想移动、删除或以其他方式操作子模块,请不要手动尝试!!!!在添加完submodule后,我们既可以向正常的项目一样push代码了。

2.2 子模块的下载

在上面的示例中,我们向现有的Git repository添加了一个新的submodule。但是,反过来,当我们需要克隆一个已经包含submodule的仓库时,又会怎么样呢?

如果我们执行普通的git clone <remote-URL>,将会下载主项目,但任何submodule文件夹都是空的!这再次生动的证明了submodle文件是独立的,不包含在它们的父仓库中。

git submodule init
git submodule update

或者在这种情况下,要在克隆了父仓库之后填充submodule,可以简单地执行git submodule update --init --recursive。不过更好的方法是在调用git clone时直接添加--recurse-submodules选项。

git submodule update --init --recursive

2.3 子模块的更新

从父仓库进入到modules目录(这个目录是submodule生成的),调用”git pull origin master“

cd modules
git pull origin master

然后就可以看到代码已经被更新到父仓库了
在这里插入图片描述

2.4 子模块的删除

有时子模块的项目维护地址发生了变化,或者需要替换子模块,就需要删除原有的子模块,如果需要删除则需要使用下面的指令

# 逆初始化模块,其中{MOD_NAME}为模块目录,执行后可发现模块目录被清空
git submodule deinit {MOD_NAME} 
# 删除.gitmodules中记录的模块信息(--cached选项清除.git/modules中的缓存)
git rm --cached {MOD_NAME} 
# 提交更改到代码库,可观察到'.gitmodules'内容发生变更
git commit -am "Remove a submodule."

此外,你可能还需要删除 .git/modules/{MOD_NAME}的缓存,否则无法创建同名的module.
或者也可以使用下面的方法:

rm -rf {MOD_NAME} #删除子模块目录及源码
vi .gitmodules #删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config #删除配置项中子模块相关条目
rm .git/module/* #删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可
git rm --cached {MOD_NAME} #执行完成后,再执行添加子模块命令即可,如果仍然报错

3. 常见问题

fatal: No url found for submodule path ‘xxx/xxx’ in .gitmodules
这种问题一般是.gitmodules内容内容错误,尝试修改文件格式

[submodule "xxx/xxx"]
    path = xxx/xxx
    url = http://xxx.xxx.xxx.git

并重新执行命令

git submodule init
# Submodule 'xxx/xxx' (http://xxxx/xxx/xxx.git) registered for path 'xxx/xxx'
git submodule sync
# Synchronizing submodule url for 'xxx/xxx'
git submodule update
#Cloning into '/Users/xxx/xxx/xxx'...
#Submodule path 'xxx/xxx': checked out '39cabde3d5c8aeba5623424asd7f5948e7f515f9f28db'

git submodule: already exists in the index

在 github 上面 clone 一个包含有 submodule 的 repo 的时候,用如下命令更新 submodule:
git submodule update —remote —merge,但是发现一直没法更新,尝试重新 add submodule,比如

git submodule add  https://github.com/cisco/openh264 open_source_code/openh264

此时一直报如下错误:

'open_source_code/openh264' already exists in the index

遇到这种情况就需要执行如下命令,解决此问题:

 rm ../.gitmodules
git rm -r --cached open_source_code/openh264

4. 参考链接

https://blog.csdn.net/weixin_34107739/article/details/91806917

https://blog.csdn.net/weixin_44901565/article/details/123086226

https://www.jianshu.com/p/2d74a6f41d07

https://segmentfault.com/a/1190000040338658

https://www.bbsmax.com/A/KE5QrnVkdL/