如何将Git子模块作为独立仓库完整克隆?
问题背景
子模块可以作为独立仓库添加到主仓库中,此时每个子模块都拥有自己的.git目录:
[submodule-test] $ mkdir super-repo [submodule-test] $ cd super-repo/ [super-repo] $ mkdir sub-repo [super-repo] $ cd sub-repo/ [sub-repo] $ git init Initialized empty Git repository in /home/foobar/tmp/submodule-test/super-repo/sub-repo/.git/ [sub-repo] $ touch foo.txt [sub-repo] $ git add foo.txt [sub-repo] $ git commit -m "* initial commit" [main (root-commit) 60c3e14] * initial commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 foo.txt [sub-repo] $ cd .. [super-repo] $ git init Initialized empty Git repository in /home/foobar/tmp/submodule-test/super-repo/.git/ [super-repo] $ git submodule add ./sub-repo sub-repo Adding existing repo at 'sub-repo' to the index [super-repo] $ git commit -m "* initial commit" [main (root-commit) 826f205] * initial commit 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 sub-repo [super-repo] $ file sub-repo/.git sub-repo/.git: directory
但克隆主仓库后,子模块的.git会变成指向主仓库的文本文件,不再是独立目录。以下克隆命令的参数是为了:
- 允许文件系统内克隆
- 模拟真实场景:存在大量嵌套远程子模块,无法逐个处理,需要自动化操作,且子模块默认分支各不相同
[super-repo] $ cd .. [submodule-test] $ mkdir clone-of-super [submodule-test] $ cd clone-of-super/ [clone-of-super] $ git -c protocol.file.allow=always clone --recurse-submodules --remote-submodules ../super-repo Cloning into 'super-repo'... done. Submodule 'sub-repo' (/home/foobar/tmp/submodule-test/clone-of-super/../super-repo/sub-repo) registered for path 'sub-repo' Cloning into '/home/foobar/tmp/submodule-test/clone-of-super/super-repo/sub-repo'... done. Submodule path 'sub-repo': checked out '60c3e1457a183d14a6af4ba9472e99d99e12de94' [clone-of-super] $ cd super-repo/ [super-repo] $ file sub-repo/.git sub-repo/.git: ASCII text [super-repo] $ cat sub-repo/.git gitdir: ../.git/modules/sub-repo
核心问题:如何克隆仓库,使得子模块作为拥有独立.git目录的独立仓库被克隆?
解决方案
Git默认把子模块的仓库数据存放在主仓库的.git/modules目录下,用gitdir文件指向该路径,目的是节省空间和统一管理。如果要让子模块恢复独立的.git目录,可以通过以下方法实现:
方法1:克隆后手动迁移单个子模块的.git目录
针对单个子模块,执行以下步骤:
- 进入子模块目录:
cd sub-repo
- 查看当前关联的git仓库路径:
cat .git
- 将主仓库
.git/modules下的子模块仓库目录,移动到当前子模块目录下并重命名为.git:
mv ../.git/modules/sub-repo .git
- 重置子模块确保版本关联正常:
git reset --hard
方法2:脚本批量处理嵌套子模块
如果有大量嵌套子模块,编写shell脚本自动化处理:
创建fix-submodules.sh脚本:
#!/bin/bash # 递归遍历所有子模块,迁移.git目录 git submodule foreach --recursive ' # 提取当前子模块关联的仓库路径 GIT_REPO_PATH=$(cat .git | sed "s/gitdir: //") # 移动仓库目录到子模块本地 mv "$GIT_REPO_PATH" .git # 重置子模块以修复关联 git reset --hard '
赋予执行权限并运行:
chmod +x fix-submodules.sh ./fix-submodules.sh
方法3:修改Git配置指定子模块.git位置
可以针对特定仓库配置子模块的.git目录路径:
# 为当前仓库的sub-repo子模块设置独立.git目录 git config submodule.sub-repo.gitdir sub-repo/.git
该方式需要提前知晓子模块名称,适合少量已知子模块的场景,大量嵌套子模块推荐使用脚本批量处理。
内容的提问来源于stack exchange,提问作者jhu
相关产品推荐
相关产品推荐

