submodule子模块分享
# submodule子模块分享
# 简介
为什么需要用子模块,什么场景下需要使用子模块?
- 当项目越来越庞大之后,不可避免的要拆分成多个子模块,我们希望各个子模块有独立的版本管理,并且由专门的人去维护。这样可以提高开发效率,降低代码的耦合性,提高了代码的复用。
有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。
Git 通过子模块来解决这个问题。 子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
# 链接资料
- 知乎
Git: submodule 子模块简明教程 (opens new window)
- git 官网
# 1. 添加
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
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
2
# 1.1 添加_子模块_
通过在 git submodule add
命令后面加上想要跟踪的项目的相对或绝对 URL 来添加新的子模块
git submodule add [url] [path]
git submodule add https://github.com/iphysresearch/GWToolkit.git
2
3
如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径。
添加子模块后,若运行 git status
,可以看到主仓库目录中会增加一个文件 .gitmodules
,这个文件用来保存子模块的信息。
在 .git/config
中会多出一块关于子模块信息的内容:
该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射。如果有多个子模块,该文件中就会有多条记录。 要重点注意的是,该文件也像 .gitignore
文件一样受到(通过)版本控制。 它会和该项目的其他部分一同被拉取推送。 这就是克隆该项目的人知道去哪获得子模块的原因。
新生成的还有相关子模块的文件:.git/modules/test2/
。
当项目添加完子模块后,需要将本次的添加子模块提交并推送到仓库:
$ git commit -m 'add submodule'
$ git push origin master
2
注意:如果你是旧版 Git 的话,你会发现 ./test2
目录中是空的,你还需要在执行一步「更新子模块」,才可以把远程仓库项目中的内容下载下来
$ git submodule update --init --recursive
# a. 创建子模块时指定一个分支
通过 -b 指定对应分支
git submodule add -b master [URL to Git repo];
# 1.2 克隆含有子模块的项目
接下来我们将会克隆一个含有子模块的项目。 当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件。其中有子模块的目录,不过是空的。
必须运行两个命令:
git submodule init
初始化子模块git submodule update
更新子模块
也可以合并成一步git submodule update --init
git clone https://github.com/chaconinc/
不过还有更简单一点的方式。 如果给 git clone
命令传递 --recurse-submodules
选项,它就会自动初始化并更新仓库中的每一个子模块, 包括可能存在的嵌套子模块。
git clone --recurse-submodules https://github.com/chaconinc/MainProject
如果还要初始化、抓取并检出任何嵌套的子模块, 请使用简明的 git submodule update --init --recursive
。
# 2. 删除子模块
删除子模块比较麻烦,需要手动删除相关的文件,否则在添加子模块时有可能出现错误同样以删除 test2
子模块仓库文件夹为例:
- 删除子模块文件夹
$ git rm --cached 子模块名称 删除缓存断开联系
$ rm -rf test2
2
- 删除
.gitmodules
文件中相关子模块的信息,类似于:
[submodule "test2"]
path = test2
url = https://gitee.com/yyzlt/test2.git
2
3
- 删除
.git/config
中相关子模块信息,类似于:
[submodule "test2"]
active = true
url = https://gitee.com/yyzlt/test2.git
2
3
- 删除
.git
文件夹中的相关子模块文件
$ rm -rf .git/modules/test2
# 3. 更新子模块
# 3.1 单独修改子模块代码
当我们修改子模块仓库合并代码后,主项目中的子模块不会自动拉取最新代码,需要手动指定和变更。
正确做法:
进入子模块的目录
git submodule update --remote
检出到想要的分支或提交
如果有修改子模块的代码需要先提交(拉取最新代码)
当运行 git submodule update --remote
时,Git 默认会尝试更新 所有子模块。
所以如果有很多子模块的话,你可以传递想要更新的子模块的名字。
如果子模块有嵌套的子模块,则应使用 -recursive
选项。
更新项目内子模块到最新版本:
$ git submodule update
更新子模块为远程项目的最新版本:
$ git submodule update --remote
子模块中有嵌套的子模块:
git submodule update --remote --recursive
# 3.2 在项目中更新子模块
直接在主项目中修改子模块的代码,并提交更新。
在主项目中提交子项目版本
0、进入子模块的目录
1、创建分支
git branch new2
2、切换分支
git checkout new2
3、修改代码
4、添加文件
git add .
5、提交
git commit -m "说明信息"
git push origin new2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以看到,其操作流程是和普通项目的提交一样的。
需要留意的是,在终端 Git 命令操作下,位于主仓库目录中除了子模块外的任何子目录下进行的 commit 操作,都会记到主仓库下。只有在子模块目录内的任何 commit 操作,才会记到子模块仓库下。
# 4. 查看子模块
要查看当前代码仓库所使用的子模块及其状态,除了看 .gitmodules
文件外,还可以执行 git submodule
命令。
$ git submodule
c1199300ee3a7c895a27f351352eabcf6dc543bd test2 (heads/master)
2
# 5. 将目录转化为子模块
随着仓库使用的越来越久,仓库中的子目录越来越多,仓库也变得越来越大。会导致pull和push的时间变长,共享代码就会变得很痛苦。
可以用将子目录独立成一个submodule的方式,保留分支和提交历史。
步骤:
- Clone仓库到本地目录
git clone https://gitee.com/yyzlt/test1.git
我想要把test3这个子目录submodule化
- 选择要保留的分支
通常刚clone下来的仓库本地只会有一个branch:master,如果我们希望在马上要做的子模块中保存其他的分支,那就首先把它们创建出来
git branch release
git branch fix
# 保存其他分支
2
3
最后origin
这个remote是不需要的,把它删除了
git remote rm origin
- 转化成submodule
$ git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter test3 -- --all
该命令过滤所有历史提交,保留对test1子目录有影响的提交,并且把子目录设为该仓库的根目录。下面解释下各参数意思:
--tag-name-filter cat 该参数控制我们要如何保存旧的tag,cat表示原样输出。
--prune-empty 删除空的(对子目录没有影响的)的提交
-subdirectory-filter 文件名称 指定子模块路径
--all 该参数必须跟在--后面,表示对所有分支做操作,如果你只想保存当前分支,就不需要这个参数了
该命令执行完毕后,查看当前目录结构就会发现里面已经是子目录的内容了。git log查看提交历史已经正常保存了。
- 清理不需要的object
上面的步骤完成后,当前仓库里还保存了很多不需要的object, 需要做下清理,才能减少当前仓库的体积。
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
2
3
4
5
提交子模块代码
首先要在gitee上创建名称为test3的仓库
对应remote
git remote add origin https://gitee.com/yyzlt/test3.git
- 提交
$ git push -u origin --all
$ git push -u origin --tags
2
3
添加submodule
再clone一份原来的仓库
删除子目录test3,并删除联系
git rm -r --cached test3
- 把test3仓库以submodule的方式添加
git submodule add https://gitee.com/yyzlt/test3.git
- 提交修改
$ git commit -m 'add submodule'
$ git push origin master
2
# 6. 子模块批量处理
如果一个项目有多个子模块,主模块和子模块分支不同,一个个手动切换分支很麻烦
git submodule foreach
简单来说 git submodule foreach
它能在每一个子模块中运行任意命令
所有的子模块都切换到 dev
分支
git submodule foreach git checkout dev
拉取所有子模块的代码
git submodule foreach git pull
但是 foreach
是遍历所有的子模块,我们要在所有的子模块下进行执行,势必会有场景中提到的报错情况,当报错后就会退出执行。我们可以在后面加条件报错后不直接退出,而是继续执行。
git submodule foreach '其他命令 || true'