若Git子模块的远端地址发生了变化本地应该怎么调整
文章目录
- 前言
- git submodule 相关命令
- 解决方案
- 怎么保存子模块的版本呢
- 总结
前言
这个问题复杂在既有Git又有子模块,本身Git的门槛就稍微高一点,再加上子模块的运用,一旦出现这种远端地址发生修改的情况会让人有些懵,不知道怎么处理,通常会乱改一通,有时候好使有时候不好使,并不清楚其中的缘由,退一步讲,如果是一个单一的Git库,如果远端地址发生了变化,使用 git remote set-url origin git@new_xxx
命令就可以就行修改,但是这个命令在子模块情况下行不通,为了说明子模块影响哪些配置文件,接下来会简单罗列下git子模块相关命令。
git submodule 相关命令
向一个项目中添加子模块:
git submodule add git@github.com:zzz/xxx.git xxx
执行之后会 clone 该子模块对应的远程项目文件到本地父项目目录下的同名文件夹中(./xxx/),父项目下也会多一个叫 .gitmodules
的文件,其内容大致如下:
[submodule "xxx"]
path = xxx
url = git@github.com:zzz/xxx.git
同时父项目下的 .git 目录中也会新增 modules/xxx/
目录,里面的内容对应子模块仓库中原有的 .git 目录中的文件,此时虽然子模块目录下的 .git 依然存在,但是已经从一个文件夹变成了文件,内容如下:
gitdir: ../.git/modules/xxx
即指向了父项目的 .git/modules/xxx
目录,如果运行 git config --list
查看项目的配置选项,会发现多了类似下面两行的内容:
submodule.xxx.url=git@github.com:zzz/xxx.git
submodule.xxx.active=true
如果修改 submodule.xxx.url
的值,则会覆盖 .gitmodules
文件中对应的 url 值;
这其实是主(父)工程 .git/config
文件中的内容,而在主工程的 .git/modules/xxx/config
文件中会有下面这些内容:
[remote "origin"]
url = git@github.com:zzz/xxx.git
fetch = +refs/heads/*:refs/remotes/origin/*
总结来说子模块的远端地址出现在三个文件中 .gitmodules
、.git/config
和 .git/modules/xxx/config
记住这些地方,我们后续会用到,这就是让人迷惑的地方。
接下来按照以下命令查看当前项目下的子模块:
git submodule status
结果格式为:
b01bf0c72235aba2e92e5c5f5173dd4cae9b374c xxx (heads/master)
如果将父项目推送到远程仓库(如gitlab),在网页浏览该项目时子模块所在的目录会多一个类似 @b01bf0c 的后缀,即上面查看子模块命令输出内容的 hash 值的前面部分,点击这个目录会跳转到这个子模块对应的仓库地址。
如果执行以下删除了子模块的命令:
git submodule deinit
再次查看时输出会是这样的:
-b01bf0c72235aba2e92e5c5f5173dd4cae9b374c xxx
在取消初始化子模块后,子模块目录会变为空目录,避免该子模块的文件继续占用空间。
解决方案
聊了这么多,总要给出一个解决方案:
** 修改主工程下 .gitmodules
文件内的远端地址 url = git@newdomain.com:zzz/xxx.git
,然后执行 git submodule sync
命令即可完成 **
这样操作之后 .git/config
和 .git/modules/xxx/config
文件中的远端的地址会一起被修改掉。
多说一点:
在执行 git submodule init
或 git submodule sync
命令时,Git 会将 .gitmodules
中的配置同步到 .git/config
中,以确保 .git/config
的信息是最新的。
而在执行 git submodule update
进行更新时,Git 主要使用 .git/config
中的子模块配置来操作子模块。
当进入子模块目录操作git时,Git使用的是 .git/modules/xxx/config
中的远端地址来进行更新和推送
怎么保存子模块的版本呢
了解了子模块的机制,我们知道主工程只是保存了一个子模块工程的版本号,但是前面看了这么多配置文件,里面只有子模块的本地路径和远端地址,没有看到版本号啊?接下来我们找一下:
首先查看最新的提交commit,得到 5c997695ed383ea52879a17f0ef6944bf99d374f
$ git log -1
commit 5c997695ed383ea52879a17f0ef6944bf99d374f (HEAD -> dev, origin/dev)
Author: demo <demo@gameup.com>
Date: Mon Nov 4 11:42:25 2024 +0800
update cmake file
利用 cat-file -p
命令查看指定 commit id
的仓库状态
$ git cat-file -p 5c997695ed383
tree 8499d7cb5c4811918bfc1341bb869d8bb38c40ef
parent b287b7783e1d44a6149de132142b88bf92b95bb3
author demo <demo@gameup.com> 1730691745 +0800
committer demo <demo@gameup.com> 1730691745 +0800
update cmake file
从结果中可以看出提交人的信息,父提交id b287b7783e1d44a6149de132142b88bf92b95bb3
,以及一棵树tree 8499d7cb5c4811918bfc1341bb869d8bb38c40ef
,怎么理解这棵树呢?其实可以类比文件系统中的文件夹,每一个commit id实际上对应着项目文件夹的版本,其中包含子文件夹版本和各个文件的版本,那么tree 8499d7cb5c4811918bfc1341bb869d8bb38c40ef
可以认为是项目文件夹内所有内容的Hash
接下来再利用 cat-file -p
命令来看看这棵tree 8499d7cb5c4811918bfc1341bb869d8bb38c40ef
$ git cat-file -p 8499d7cb5c4811918bfc1341bb869d8bb38c40ef
100644 blob 344bc6e4d42e8fbae98a8d808b177b6773f88d31 .gitattributes
100644 blob c634b5c2da75f0eed128c701530afa5b2b799c69 .gitignore
100644 blob ba0ab2fd274f219c0e007177ad861f77a57581b0 .gitmodules
040000 tree 90317b8c9672803678037d9f006ea6818d90210b .vscode
040000 tree 0706cb16e4c162b194680972ab7605654a1541a1 cmake
160000 commit cec7534dcb171d38d46ba47217694261ad4c7b15 xxx
040000 tree 2c693865de8c02cd87f28fe85ba5e57a73617029 src
这里边有3类内容,分别是 blob
、tree
、commit
,blob就是具体的文件,tree就是代表一个文件夹,而 xxx
的类型是一个commit,从上文我们知道 xxx
是包含了另一个项目的文件夹,如果是一个单纯的文件夹这里的类型应该是tree,但是因为是子模块,所以类型是记录另一个项目commit id,这样我们就找到了主工程引用子模块的版本存储的位置了。
总结
- 添加子模块的命令
git submodule add git@github.com:zzz/xxx.git xxx
- 初始子模块
git submodule init
,查看子模块git submodule status
- 注销子模块
git submodule deinit xxx
,注销后文件夹清空,但是配置文件需要手动删除 - 子模块远端地址发生变化,手动修改
.gitmodules
文件中地址,然后执行git submodule sync
再提交修改
放下助人情节,尊重他人命运,可怜之人必有可恨之处,子非鱼,安知鱼之经历与内心~