Git submodule的使用

4,952 阅读4分钟

最近在做一个上传的客户端,上传的部分由后端同学以 SDK 的方式提供,因此该 SDK 是在一个独立的仓库,那么对于客户端该如何方便的集成该 SDK 呢?每次 SDK 更新把代码拷贝到客户端仓库?把 SDK 发布到 npm?这就可以考虑用 git 的 submodule。

什么是 submodule

submodule 是一个多项目管理工具,它允许将子项目以独立的 git 项目添加到主项目,而主项目以 submodule 的形式拥有子项目。子项目拥有自己的 commit、push、pull,而与主项目互不干扰。主项目只需要记录子项目的地址和所需要的 commit id,通过地址和 commit id 就能够得到对应的子项目。

添加 submodule

通常情况下,我们都有一个主项目(MainProject),在 MainProject 文件夹下执行如下命令,即可添加 submodule。

/*
  url: 子项目远程地址或本地地址
  path: 子项目路径,可省略
*/
$ git submodule add [url] [path]
例: git submodule add git@github.com:fengyueran/UploaderSDK.git ./src/UploaderSDK

git status 可以看到如下信息

 On branch master
    Changes to be committed:

        new file:   .gitmodules
        new file:   UploaderSDK

可以看到多了两个文件: .gitmodules 和 UploaderSDK。 cat .gitmodules 看到.gitmodules 储存了 submodule 的路径及远程地址。

[submodule "src/uploaderSDK"]
	path = src/uploaderSDK
	url = git@github.com:fengyueran/UploaderSDK.git

UploaderSDK 的内容为 submodule 的 commit id。

Subproject commit 6b53e1840b27ca1587b96c1eb9dd5f4ff0866089

不难想象通过.gitmodules 和 UploaderSDK 的信息就可以拿到 submodule 的内容了,因此我们需要提交这个两个文件。

git add .
git commit -m "add submodule"

克隆带有 submodule 的项目

主要有两个方式

1. 采用先克隆后更新的方式

和想象中的不一样,直接 clone 主项目,submodule 并不会跟着 clone 下来,而只有包含 submodule 名的空文件夹。

1)$ git clone git@github.com:fengyueran/MainProject.git

需再执行如下命令

2)$ git submodule init

输出如下,可以看到该命令给子项目注册了路径,即在主项目中的位置。此时,uploaderSDK 文件夹仍未空。


    Submodule 'src/uploaderSDK' (git@github.com:fengyueran/UploaderSDK.git) registered for path 'src/uploaderSDK'

 再执行

//该命令并不是直接更新到最新的submodule commit,而是更新至主项目所存储存的commit(有可能是较旧的commit)。
3)$ git submodule update

输出如下,可以看到 sumodule 得到更新,更新到主项目存储的 submodule commit,是一个游离的 git header。

Cloning into '/Work/test/MainProject/tmp/MainProject/src/uploaderSDK'...

Submodule path 'src/uploaderSDK': checked out '6b53e1840b27ca1587b96c1eb9dd5f4ff0866089'

2. 采用递归参数--recursive

git clone git@github.com:fengyueran/MainProject.git --recursive

输出如下,可以看到主项目包括 submodule 都被 clone 下来了。

Cloning into 'MainProject'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (4/4), done.
Receiving objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 4 (delta 0), pack-reused 0
Submodule 'src/uploaderSDK' (git@github.com:fengyueran/UploaderSDK.git) registered for path 'src/uploaderSDK'
Cloning into '/Work/test/MainProject/tmp/MainProject/tmp/MainProject/src/uploaderSDK'...
Submodule path 'src/uploaderSDK': checked out '6b53e1840b27ca1587b96c1eb9dd5f4ff0866089'

修改更新 submodule

主要有两种情况

1. 直接在主项目中的 submodule 下修改

如上例,直接在 MainProject 下的 src/uploaderSDK 中修改,uploaderSDK 切换到工作分支,修改并提交后,可以 checkout 到最新的 commit,也可以不切,反正都在当前最新的 commit 上(如果想测试其他 commit 也可以切换到相应 commit 上),此时 MainProject 中我们可以看到 src/uploaderSDK 的 commit 有如下变化

-Subproject commit 6b53e1840b27ca1587b96c1eb9dd5f4ff0866089
+Subproject commit a4d6dc0457673a275b1f6cbeda6f8ff23293b9de

a4d6 为修改的提交,需要注意的地方是此时 submodule 已经在最新的 commit 上了,不要再在 MainProject 中 git submodule update 进行更新了,如果进行此操作 submodule 又会回到原来的 commit(带有减号的 commit),只需要在 MainProject 提交,并在必要的时候 push 到远程仓库。这种方法,非 submodule 的开发人员就不用关心 submodule 是否更新了,只需要在 MainProject 下 pull 代码发现 submodule 有更改时执行 git submodule update(更新为带减号 commit)进行更新,前提是其他开发人员提交了正确的 submodule commit。

2. 在 submodule 自己独立的仓库进行修改

在工作目录克隆下 submodule 的仓库,切换到工作分支进行修改提交并 push 到远程仓库。这种方法需要 submodule 开发人员告诉 MainProject 的开发人员 submodule 有更新或主动查看是否有更新,有更新时就在 MainProject 的 src/uploaderSDK 下 pull 远程代码(需要知道 submodule 的工作分支),快速合并后,uploaderSDK 的 commit 有如下变化,此时同 1 不要 git submodule update,而只是在 MainProject 下提交这个更改。

-Subproject commit f4573cc1bb50000779202c7f56a640b1ffc075cb
+Subproject commit 64ae6d149c0f6e3b06b8cea262c6126a7bc0887f

删除 submodule

执行如下命令

  1. $ git submodule deinit
逆初始化模块,submodule为子模块目录,执行后可发现子模块目录被清空
$ git submodule deinit [submodule_name]
-> Cleared directory 'test2sub'
Submodule 'test2sub' (git@github.com:fengyueran/test2sub.git) unregistered for path 'test2sub'

//执行如下命令还能看到子项目信息
$ git submodule
-> -dab52c62f52353d9967619625c28e28dc4320aef test2sub
  1. $ git rm --cached [submodule_name]
// 删除.gitmodules中记录的模块信息(--cached选项清除.git/modules中的缓存)
git rm --cached test2sub
//执行如下命令已看看不到删除的子项目信息了
$ git submodule
  1. $ git commit
git commit -m "remove submodule"