[[toc]]
git submodules 子模块
如果想要在一个git项目中包含有其他git项目(比如一些基础框架库, 组件库,共用代码等), 可以使用git submodules
进行管理。本文就git submodules
常用用法做一些记录,阅读前可以先查看官网文档, 指令详解.
个人感觉: git子模块跟tag非常像。
git子模块初始化
如果想要为当前模块添加一个子模块, 可以执行add
指令 :
git submodule add <repository> [<path>]
如:
git submodule add https://github.com/hscfapps/vue-app-plus-inner.git
它会在当前项目下创建一个vue-app-plus-inner
的目录, 该目录就用于存放目标库的代码。如果你需要改一下存放子git项目目录名, 可以:
git submodule add https://github.com/hscfapps/vue-app-plus-inner.git source-app
默认会拉去目标库的master
分支, 如果你要拉去指定分支, 可以在执行命令行时添加选项:
git submodule -b release add https://github.com/hscfapps/vue-app-plus-inner.git
这样就会拉去release分支。
执行指令后, 还会生成一个.gitmodules
文件, 该文件用于存放git submodules
的一些配置, 可以直接该这里面的配置, 但不安全,更加建议通过提供的git submodules
修改一些配置。
详细可以查看
.gitmodules
文件说明。
如果你是克隆一个含有子模块的git项目, 默认克隆下来, 会包含.gitmodules
文件和子模块目录名, 但是目录文件夹下没有任何内容, 需要执行:
git submodule init
git submodule update
这个时候就能将子模块的代码拉去下来了。
上面有一个简便方案,在克隆时添加--recursive
选项:
git clone --recursive https://github.com/chaconinc/DbConnector
该项目中的子模块以及嵌套子模块都会被初始化, 不需要在去执行init
, update
操作了
update
用于跟新代码,但只会跟新代码到当前的项目中保存的子模块的commit
(这在官方文档中有说明, git会将子模块视为有特殊的文件,记录它的commit,而不是把他作为一个目录).但git服务器上通常会比本地多几次提交, 所以可以添加--remote
选项, 来强制将子模块更新到当前分支最新的提交:
git submodule update --remote
如果子模块在本地有修改, 那么拉取代码就会失败, 你可以指定:
git submodule update --remote --merge
就会在拉去下来后跟本地合并。
git submodule update --remote
其实相当于在每个子模块目录下执行git pull
操作,且是将服务器上每个分支的代码都更新了下来。
还需要注意一点的时, 执行git submodule update
, 会对所有的子模块都生效, 如果你需要只对某一个子模块生效, 可以
git submodule update <options> moduleNmae
至于何时使用git submodule update --remote
,取决于当前子模块是固定版本升级还是采取自动升级策略, 如果是固定版本升级, 只有在git项目中需要升级子模块时, 才执行git submodule update --remote
(其实更好的办法时进入该子模块目录下,手动升级到指定的commit,实现精准版本控制)。如果时自动升级, 那么在每次变更代码时, 都可以执行git submodule update --remote
将子模块升级到最新的提交, 然后提交代码到服务器上。
子模块分支切换
在上文中,说过可以在初始化子模块时指定分支。 在实际开发过程时,会碰到分支需要变更的需求,git submodule
提供set-branch
指令来改变子模块的分支:
git submodule set-branch -b branchName sub-module-name
改变之后,需要注意的是当前子模块的commit
并没有发生变化, 需要执行
git submodule update --remote
更新commit。
所以set-branch
指令更像是在.gitmodules
里面将分支的配置修改一下。
发现--default选项不能使用,可能是我使用的方式不对。
在子模块中的代码操作
进入子模块后, 就是进入了一个新的git
库, 可以执行任何git指令。生成新的commit之后,在推送到子模块的git服务器库之后, 如果当前项目需要使用, 还是要推送当前项目的最新代码。
gitsubmodule作为版本发布器
当前clone一个带有子模块的git项目时, 初始化子模块后, 进入子模块中, 执行git status
会发现, 当前子模块的git head
处于游离状态,指向一个commit
,跟tag
的状态十分相似。 所以子模块其实记录的都是commit
点,跟分支没有关系, 所以上文中的分支操作你都可以不需要去管他们。
当我们需要更新一个子模块时, 可以在当前项目中进入子模块, 然后通过平常的git指令将子模块项目执行目标commit
点,然后在git项目中提交,推送到服务器上, 这样子模块就升级了(跟tag作为版本发布的点有点相似)。
基于commit
而不是基于branch
的子模块天然支持版本发布的特点。
子模块遍历执行指令
如果项目中有多个子模块, 在执行一些操作时, 需要进入每个子模块目录下执行指令, 十分的模范。
git submodule
提供foreach
指令, 支持在每个库中执行相同的git指令:
git submodule foreach git status
或者:
git submodule foreach "git status"
嵌套子模块
在克隆项目时, 添加选项--recursive
,可以递归的初始化嵌套子模块。
另外一些git submodules
指令支持--recursive
, 也都支持对嵌套子模块操作。
删除子模块
可以首先执行:
git submodule deinit -f sub-module-name
删除全部:
git submodule deinit -f --all
然后执行:
git rm sub-module-name
如果上述操作还不能删除子模块, 按照下述的步骤手动操作一遍:
rm -rf 子模块目录 删除子模块目录及源码
vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config 删除配置项中子模块相关条目
rm .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可
git rm 子模块名称