背景
浏览开源库的时候经常会看到如下子模块的引用情况。
子模块通常是项目比较复杂,需要对项目进行拆分,而项目又有引用关系时会使用。通常拆分项目后,我只需要关注自己的项目更改,不需要关注引用的项目都做了哪些更改。
通常这样拆分后,项目就不会在一个 git
仓库中,这时用 submodule
来管理代码仓库会清晰方便许多。
基本使用
添加子模块
git submodule add <url> <本地路径>
,例:
git submodule add https://github.com/grassto/example.git example
执行完毕后,发现仓库目录下多了个 example
目录,但是里面没有任何文件,此时需要再执行:
git submodule update --init --recursive
这时会看到仓库有如下变化:
可以看到 .gitmodules 中有如下内容
[submodule "example"]
path = example
url = https://github.com/grassto/example.git
另外,.git/config
中会多出一块有关子模块的信息
[submodule "example"]
active = true
url = https://github.com/grassto/example.git
同时在 .git/mudules
目录下会多出 .git/mudules/example
目录。
克隆带有子模块的项目
直接 clone
只能拉取主项目的代码,需要多执行下 submodule
相关的命令,如下两种方式:
git clone https://github.com/grassto/example.git --recurse-submodules
先克隆,再初始化子模块拉取
git clone
git submodule init
git submodule update
更新子模块
git submodule update
git submodule update --remote
不添加 --remote
参数,只更新子模块到该仓库使用的最新版本,例:
子模块一直在自己开发,更新了 1.0
, 1.1
, 1.2
版本,但是这时候我的主仓库只使用了 1.0
版本,使用 git submodule update
更新后,发现只能更新到 1.0
版本。
添加了 --remote
参数,则直接更新到子模块仓库的最新版本。
简单理解就是主仓库使用的就是特定版本的子模块仓库,若要更新,需要主仓库主动进行更新再提交。
子模块地址变动
git submodule sync --recursive
若子模块的 url
发生了改变,这时执行 git submodule update
会失败,可以使用 sync
来同步。
这个我没用过,官网上看到的,这里提一下。
删除子模块
git submodule deinit example
git rm example
git commit -m "delete submodule example"
推荐使用上面这种方式,当然也可以手动删除:
- 删除本地子模块目录
git rm --cached example
rm -rf example
- 删除
.gitmodules
子模块信息
[submodule "example"]
path = example
https://github.com/grassto/example.git
- 删除
.git/config
文件中的子模块内容
[submodule "example"]
active = true
https://github.com/grassto/example.git
- 删除
.git
文件夹中的相关子模块文件
rm -rf .git/modules/example
总结
- 使用了
submodule
后,若不主动更新,项目会一直使用固定版本的submodule
模块,需手动更新(git submodule update --remote
)。 - 若是在
go
或者其他有包管理的项目中,建议还是使用开发语言工具去做这种类似的第三方包管理会比较方便。
其他
作为一个 go
开发,我还是提议使用 go module
来做这种包管理,这里提一下我使用 submodule
的原因:
现在用到了一个包引用的是本地的,使用了 replace
特性,在做 gitlab CI
的时候,需要同步代码仓库,感觉不方便,顾使用了 git submodule
将代码作为子模块。这样就可以使用 gitlab
的 GIT_SUBMODULE_STRATEGY: recursive
特性。
如果你也是个 go
开发人员,这里不建议这么用,因为 go module
是可以引用私有库的,我这样用是有历史原因的。
我需要引用的库的 go.mod 如下,module
的 name
是 example
而不是 github.com/example
module example
go 1.18
require ......
使用该模块的时候都是将其拉到本地,然后 replace
。
module work
go 1.18
replace example => ./example
参考
- 官方文档 Git-Tools-Submodules
- Git中submodule的使用