git submodule 使用场景汇总

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

指定了vmtouchsupport-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做一些测试,这个时候也可能需要变更子模块的分支,或者将最新的代码取下来。

  1. 拉取最新的submodue 代码方式

    cd vmtouch
    git fetch # 获取最新的代码
    git merge orgin/master # 合并最新的代码
    
  2. 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,需要注意每行的符号

git submodule 使用场景汇总

如果用普通的换行符是无法拥有这样的特殊符号的,会导致新添加的submodule 之后无法更新。

建议完整复制之前的submodule entry内容,再在其基础上修改

或者使用场景二场景四 来添加新的submodule git submodule add ...等。

之后再执行git submodule update --remote

3. 总结

关于submodule 的使用基本就是以上几种场景,如果后续还会碰到其他的场景或者问题,欢迎大家留言补充。

上一篇:git submodule的使用和优势


下一篇:composer包加载后开发环境正常,推到服务器上报错(git-submodule)