Git Submodule

一. 什么是Git Submodule

在实际工程中,使用别人的代码模块是一件很常见的事情,比如我有一个git项目,名为A,GitHub上有一个项目B可以作为我的子模块,传统的思路可能是,把别人的项目BClone到自己的本地仓库,然后一起作为自己的仓库内容进行提交,但是这样有很多不方便的地方,比如:

  • 如果项目B更新了,那么自己还得手动Clone复制粘贴再上传
  • 如果模块很多时,每次拉取工程,都要拉取整个项目A,包括子项目B、C、D等
  • 可能会造成版权问题(我猜的)

Git Submodule允许在自己的Github仓库里加入别人的github仓库,作为自己仓库的子仓库(即submodule),有了Git的Submodule功能,就可以解决上述问题,git submodule允许在git仓库里存放别人仓库的url,作为自己的子模块,其核心内容是在Git仓库里面加入一个.gitmodules文件,如下图所示:
在这里插入图片描述

.gitmodules只是一个文本文件,用来记录仓库里的submodule的url和相对于仓库的路径,举个例子,一共有三个submodule,很简单的记录了各个submodule的名字、路径和对应的地址url:

[submodule "Hazel/vendor/spdlog"]
	path = Hazel/vendor/spdlog
	url = https://github.com/gabime/spdlog.git
[submodule "Hazel/vendor/GLFW"]
	path = Hazel/vendor/GLFW
	url = https://github.com/TheCherno/glfw.git
[submodule "Hazel/vendor/imgui"]
	path = Hazel/vendor/imgui
	url = https://github.com/hwx0000/imgui.git

简单来说,git submodule的核心就是这个.gitmodules文件,下面介绍常用的相关git命令,看看具体怎么操作。



二. 添加submodule

直接在自己的git的cmd或者bash界面,输入git submodule add 要拉取的子模块的url 想要存放的相对路径,就可以在自己的git仓库里添加别的仓库作为submodule了:

git submodule add url path

举个例子,添加Github上的imgui库作为我的submodule,放到我工程的imgui文件夹中:

git submodule add https://github.com/hwx0000/imgui.git imgui

然后等待git去下载就好了,不过如果网不好,很容易失败,下载完成后,工程根目录应该会自动出现一个.gitmodules文件,然后可以把它提交,push到github就可以了。


三. Git Clone带有submodule的仓库

前面提到了怎么直接从自己的仓库添加submodule,下面介绍如何Clone一个带有submodule的git仓库。

如果要从其他电脑clone仓库,仅仅输入git clone url只会拉取submodule对应的文件夹,里面的内容是空的,要想拉取里面的内容,有以下做法。

Clone仓库,同时Clone仓库里所有的submodule
在命令行输入git clone --recursive url,url是实际的仓库地址,就可以了,仅仅加了个--recursive,recursive也就是递归的意思,这种命令下,就算里面的submodule里还有自己的submodule(套娃),也应该是可以clone到的(不过我没有具体测试过)

Clone仓库,同时Clone仓库里指定的submodule
这种情况应该也很常见,比如别人的仓库里一堆submodule,但是我并不需要用到那么多,所以我只想Clone对应的几个submodule,这个时候,就需要使用git submodule initgit submodule update指令了。如下图所示,比如原仓库有四个submodule,都是存放了lib文件的文件夹:
在这里插入图片描述
.gitmodules的内容如下:

[submodule "lib1"]
    path = lib1
    url = https://example.com/demo/lib1
[submodule "lib2"]
    path = lib2
    url = https://example.com/demo/lib2
[submodule "lib3"]
    path = lib3
    url = https://example.com/demo/lib3
[submodule "lib4"]
    path = lib4
    url = https://example.com/demo/lib4

比如我要运行code1.js文件,这个文件只使用了lib1和lib2里的.lib文件,所以我只需要拉lib1和lib2的submodule,那么可以输入这两行代码:

git submodule init lib1 lib2
git submodule update//会update所有被init过的submodule

也就是说,git默认的submodule是不会被clone的,除非你使用了git submodule init submodulename的指令,git submodule init具体做了什么,可以参考: https://stackoverflow.com/questions/44366417/what-is-the-point-of-git-submodule-init。

git clone忘了带–recursive
如果忘了带–recursive,或者是由于网络原因clone失败,可以输入:

git submodule update --init

相当于为所有的submodule进行git submodule init,然后执行git submodule update


四. 更新submodule

更新到最新版本的submodule
比如我的项目A引用了submodule项目B,然后别人最近在项目B上有了新的更改,那么输入下面命令即可:

git submodule update --remote

这行命令应该会拉取所有被git submodule init的submodule,如果只想更新特定的submodule,那么可以输入:

git submodule update --remote <path to the submodule>


五. Git移除submodule

看到网上说的步骤如下:
To remove a submodule you need to:

  • Delete the relevant section from the .gitmodules file.
  • Stage the .gitmodules changes git add .gitmodules
  • Delete the relevant section from .git/config.
  • Run git rm --cached path_to_submodule (no trailing slash).
  • Run rm -rf .git/modules/path_to_submodule (no trailing slash).
  • Commit git commit -m "Removed submodule "
  • Delete the now untracked submodule files rm -rf path_to_submodule

但实际上我是把Git加入到cmd的命令行里,用cmd环境做的,所以上面的Linux命令不适用,不过做法还是大同小异:

  • .gitsubmodules文件里手动删除对应路径
  • 用git add保存文件改动
  • 手动删除.git/config文件里面对应的内容
  • cmd里运行git rm --cached path_to_submodule (no trailing slash),不过我是带斜杠的,我写的是git rm --cached Hazel/vendor/imgui
  • 手动找到.git文件夹下的modules文件夹,删除里面的submodule的内容
  • 最后commit,再push就可以了
  • 然后别忘了删除自己的本地的submodule内容

更正式和详细的做法参考https://stackoverflow.com/questions/1260748/how-do-i-remove-a-submodule,这里就不多研究了


六. 查看submodule状态

查看当前仓库的submodule状态,可以得到各个submobule对应的commit和commit对应的哈希值,指令如下:
git submodule status
查看当前仓库的submodule状态,包括submodule的submodule状态(if you want to show nested submodules.):
git submodule status --recursive



七. 更改submodule的版本

我一开始直接用的git submodule update --remote path-to-submodule的指令,cmd界面上成功走完了,没报错,但是我进去我的submodule里,发现文件内容并没有改变,所以我想,应该是更新了对应的commits,但是我的submodule仓库的HEAD指针指向的版本没有修改。

所以我做了接下来的尝试,首先cd到submodule对应的文件夹里面,输入git status,发现显示的哈希值与我在Github上的版本哈希值一模一样,为了改变版本,我找到原submodule上对应我想要版本的哈希值,再进行了git reset --hard 哈希值的操作,结果本地文件是改过来了。

接下来的问题就是提交了,我把路径回退到主仓库,进入cmd,输入下列指令,如下图所示:
在这里插入图片描述
就可以看到我在github上仓库的哈希值更改到了我想要的commit对应的哈希值,(虽然GitHub上的Commits里显示不到具体的修改),如下图所示:
在这里插入图片描述



八. 常见问题

下载到一半退出了
如下图所示,这里的Cloning into,由于外网的下载很慢,也没有进度条,所以这里应该耐心等待,我之前就是直接关掉了,然后再用git submodule update,就会报错fatal: could not get a repository handle for submodule ....
这个时候我去把对应submodule路径的.git文件删掉,再重新git submodule update,clone之后等等就可以了
在这里插入图片描述

在git submodule里添加东西并提交
由于实际需求,尝试过从当前仓库在submodule中添加东西,后来发现这样是不对的,因为submodule本身就是一个整体,不应该自己在里面加东西。如果要使用别人的东西,正确做法是使用GitHub的Fork功能,Fork后自己Clone对应的工程,然后直接修改submodule后上传,再在自己的仓库里调用git submodule update --remote就可以了

直接pull仓库发现不会pull仓库里面submodule的内容
参见上面的git submodule update,使用submodule应该不存在pull和push指令,自己应该是无权改动的。
参考链接:https://stackoverflow.com/questions/1030169/easy-way-to-pull-latest-of-all-git-submodules

Git Submodule变更
这个稍微麻烦一点,可以参考:https://stackoverflow.com/questions/913701/how-to-change-the-remote-repository-for-a-git-submodule
git submodule sync https://stackoverflow.com/questions/16727110/forked-git-submodule-and-changed-source-url-but-clone-still-pulls-old-source-u

Submodule点进去发现404
用git clone --recursive的时候报错Fetched in submodule path ‘Hazel/vendor/imgui’, but it did not contain babcc121b86274e4e920fede44a407cd5b96df4b. Direct fetching of that commit failed.然后进我的Github仓库,点进去对应的submodule,发现Github显示404

这是因为我之前改动了submodule,而且进行了commit,但是没有push到remotes上,所以需要回去对应的submodule,用git reset --hard 回到特定的版本,重新加上对应的内容,再重新add、commit和push,然后再去submodule的父项目,重新add和commit对应submodule的内容

posted @ 2020-10-26 12:30  弹吉他的小刘鸭  阅读(1682)  评论(0编辑  收藏  举报