1. 前言
当我们想要在A项目中使用B项目作为依赖,并且希望和B项目保持同步更新。这个时候git 会将A项目作为B项目的子模块,为了保证B的更新能够在A需要的时候同步到A中,git 推出了子命令submodule,能够非常方便得让A管理 包括B、C在内的多个子模块,并且能够指定具体子模块的一个分支来给A用。
当然,除了介绍submodule 子命令的基本使用之外,也记录一下使用过程中遇到的一些比较坑的问题。
2. 基础命令介绍
2.1 场景一:已有仓库,添加一个子模块
> ls -al
total 8
drwxr-xr-x 4 zhanghuigui staff 128 Feb 5 15:21 .
drwx------@ 35 zhanghuigui staff 1120 Feb 5 15:20 ..
drwxr-xr-x 12 zhanghuigui staff 384 Feb 5 15:26 .git
-rw-r--r-- 1 zhanghuigui staff 7 Feb 5 15:21 README.md
如上已有的一个仓库,现在需要为这个仓库添加一个submodule, https://github.com/hoytech/vmtouch.git
执行如下命令,其中url 之后的vmtouch
是 指定的子模块存放的目录,如果不指定,默认是以子模块的仓库名称命名。
> git submodule add https://github.com/hoytech/vmtouch.git vmtouch
正克隆到 '/Users/zhanghuigui/Desktop/submodule_test/vmtouch'...
remote: Enumerating objects: 546, done.
remote: Total 546 (delta 0), reused 0 (delta 0), pack-reused 546
接收对象中: 100% (546/546), 362.03 KiB | 168.00 KiB/s, 完成.
处理 delta 中: 100% (301/301), 完成.
这个时候如果 执行git status
就能看到 当前仓库下已经有了vmtouch目录 和一个.gitmodules
文件
> git status
位于分支 master
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
新文件: .gitmodules
新文件: vmtouch
需要注意的是.gitmodules
是submodule的配置文件,当仓库A 内部有子模块时,就会为A自动生成一个.gitmodules来保存submodule相关的配置,并且这个文件也会被git追踪。
可以看到文件内容如下,submodule 之后的"vmtouch" 表示该submodule所在目录,path后的内容需和submodule保持一致。
> cat .gitmodules
[submodule "vmtouch"]
path = vmtouch
url = https://github.com/hoytech/vmtouch.git
想要对比cache的不同内容,可以通过git diff --cached
,可以看到尽管vmtouch是一个目录,但是git 并不会追踪其内部的数据文件。
> git diff --cached vmtouch
diff --git a/vmtouch b/vmtouch
new file mode 160000
index 0000000..3382336
--- /dev/null
+++ b/vmtouch
@@ -0,0 +1 @@
+Subproject commit 3382336a9be3ab9b3552208a3db4ada7e247b542
最后提交变更即可:
> git commit -m "[feature] add the vmtouch as submodule"
[master f198528] [feature] add the vmtouch as submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 vmtouch
> git push origin master # 提交到远端仓库
submodule的子模块也会被添加到当前仓库的config文件中:
> cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [submodule "vmtouch"] active = true url = https://github.com/hoytech/vmtouch.git
2.2 场景二:已有仓库,添加一个子模块的特定分支
我们想要为一个已经存在的仓库添加一个子模块,并且仅仅将这个子模块的指定分支添加进来,初使目录还是之前的目录
> la
total 16
drwxr-xr-x 13 zhanghuigui staff 416B Feb 5 16:06 .git
-rw-r--r-- 1 zhanghuigui staff 84B Feb 5 15:37 .gitmodules
-rw-r--r-- 1 zhanghuigui staff 7B Feb 5 15:21 README.md
drwxr-xr-x 2 zhanghuigui staff 64B Feb 5 15:58 vmtouch
添加一个子模块到指定目录,并指定其分支:
> git submodule add -b support-block-devices -- https://github.com/hoytech/vmtouch.git thirdparty/vmtouch
指定了vmtouch
的support-block-devices
分支,并 将vmtouch子模块放在了thirdparty/vmtouch 目录下。
这个时候文件又发生了变更:
> git status
位于分支 master
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: .gitmodules
新文件: thirdparty/vmtouch
可以看到又一个子模块的entry信息被添加到了.gitmodules
之中,并且多了一个模块的分支信息。
> cat .gitmodules
[submodule "vmtouch"]
path = vmtouch
url = https://github.com/hoytech/vmtouch.git
[submodule "thirdparty/vmtouch"] # 第二次添加的子模块
path = thirdparty/vmtouch
url = https://github.com/hoytech/vmtouch.git
branch = support-block-devices # 模块分支
需要注意的是,这里指定仓库的特定分支 不能是tag, tag没有commit记录,而submolue需要通过commit id来区分不同的分支。
如果是用了tag 来当作分支使用,会出现如下问题:
> git submodule add -b v1.3.1 -- https://github.com/hoytech/vmtouch.git thirdparty/vmtouch 正克隆到 '/Users/zhanghuigui/Desktop/submodule_test/thirdparty/vmtouch'... remote: Enumerating objects: 546, done. remote: Total 546 (delta 0), reused 0 (delta 0), pack-reused 546 接收对象中: 100% (546/546), 362.03 KiB | 305.00 KiB/s, 完成. 处理 delta 中: 100% (301/301), 完成. fatal: 'origin/v1.3.1' 不是一个提交,不能基于它创建分支 'v1.3.1' Unable to checkout submodule 'thirdparty/vmtouch'
最后继续执行commit和push ,将结果更新到本地和远程仓库即可。
2.3 场景三:已有仓库,更新子模块内容
工作过程中,我们同样会维护一些自己的一些工作目录,每次重新进入工作目录的时候会有想要更新子模块的需求,或者子模块中有合入了新的feature,我们想要对这个feature做一些测试,这个时候也可能需要变更子模块的分支,或者将最新的代码取下来。
-
拉取最新的submodue 代码方式
cd vmtouch git fetch # 获取最新的代码 git merge orgin/master # 合并最新的代码
-
submodule 外部更新数据
git submodule update --remote vmtouch
这个方式,其实也类似于进入submodule ,执行fetch和merge命令
2.4 场景四:已有仓库,变更子模块分支
以上仅仅是更新子模块的当前分支的内容,并不会变更子模块的分支,也就是变更.git目录中的config数据。如果这个时候需要变更子模块的分支,则可以执行如下命令:
# 变更前.gitmodules 配置
> cat .gitmodules
[submodule "vmtouch"]
path = vmtouch
url = https://github.com/hoytech/vmtouch.git
[submodule "thirdparty/vmtouch"]
path = thirdparty/vmtouch
url = https://github.com/hoytech/vmtouch.git
branch = support-block-devices
# 变更vmtouch 目录的分支配置,默认是master分支
> git config -f .gitmodules submodule.vmtouch.branch support-block-devices
> cat .gitmodules
[submodule "vmtouch"]
path = vmtouch
url = https://github.com/hoytech/vmtouch.git
branch = support-block-devices
[submodule "thirdparty/vmtouch"]
path = thirdparty/vmtouch
url = https://github.com/hoytech/vmtouch.git
branch = support-block-devices
# 执行.gitmodules 中的更新
> git submodule update --remote
Submodule path 'vmtouch': checked out '3111cb1a51a0a556dd86e4a7d4653e6198eb6a95'
> cd vmtouch
> git branch -av # 对应目录已经更新到了support-block-devices 分支
* (3111cb1) 3111cb1 support block devices
master 3382336 Merge pull request #78 from johnmay/can_do_mincore
remotes/origin/HEAD -> origin/master
remotes/origin/hp-ux-support 86ad976 Note HP-UX support
remotes/origin/master 3382336 Merge pull request #78 from johnmay/can_do_mincore
remotes/origin/support-block-devices 3111cb1 support block devices
(END)
通过修改.gitmodules 来变更对应submodule 的分支,变更之后.gitmodules文件和对应的子模块目录都会改动
> git status
位于分支 master
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: .gitmodules
新文件: thirdparty/vmtouch
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: .gitmodules
修改: vmtouch (新提交)
再次commit和push即可。
如果想要看看关于submodule的改动提交日志,可以通过
> git log -p --submodule
commit f19852812f62bbbdc9e916330d78d1021e816818 (HEAD -> master, 0a)
Author: BaronStack <2689496754@qq.com>
Date: Fri Feb 5 15:53:12 2021 +0800
[feature] add the vmtouch as submodule
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..a0e55c0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vmtouch"]
+ path = vmtouch
+ url = https://github.com/hoytech/vmtouch.git
Submodule vmtouch 0000000...3382336 (new submodule)
commit afac2efb7a6807f08ab7b83a72a4b6348a414f0b
Author: BaronStack <2689496754@qq.com>
Date: Fri Feb 5 15:21:39 2021 +0800
[feature] first file touch in rep
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2f723b7
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+#FIRST
2.5 场景五:手动修改.gitmodules不生效问题
可以通过修改.gitmodules文件来增加对应的submodules,需要注意每行的符号
如果用普通的换行符是无法拥有这样的特殊符号的,会导致新添加的submodule 之后无法更新。
建议完整复制之前的submodule entry内容,再在其基础上修改。
或者使用场景二和场景四 来添加新的submodule git submodule add ...
等。
之后再执行git submodule update --remote
3. 总结
关于submodule 的使用基本就是以上几种场景,如果后续还会碰到其他的场景或者问题,欢迎大家留言补充。