如何克隆带有子模块的本地仓库?

7

假设我递归地克隆一个仓库。

$ git clone --recursive ssh://server/project/client
Cloning into 'client'...
remote: Counting objects: 191, done
remote: Finding sources: 100% (191/191)
remote: Total 191 (delta 53), reused 159 (delta 53)
Receiving objects: 100% (191/191), 27.59 KiB | 0 bytes/s, done.
Resolving deltas: 100% (53/53), done.
Checking connectivity... done.
Submodule 'gui' (ssh://server/project/client/gui.git) registered for path 'gui'
Cloning into 'gui'...
remote: Counting objects: 3213, done
remote: Finding sources: 100% (3213/3213)
remote: Total 3213 (delta 1272), reused 3107 (delta 1272)
Receiving objects: 100% (3213/3213), 47.88 MiB | 12.05 MiB/s, done.
Resolving deltas: 100% (1272/1272), done.
Checking connectivity... done.
Submodule path 'gui': checked out '7315db8d7a8b36929f7874dc5477359839ec51ce'

现在我想创建该本地仓库的本地克隆(可能是在本地进行更改和提交之后)。
$ git clone --recursive client/ client_copy
Cloning into 'client_copy'...
done.
Submodule 'gui' (/home/deployer/client/gui.git) registered for path 'gui'
fatal: repository '/home/deployer/client/gui.git' does not exist
Clone of '/home/deployer/client/gui.git' into submodule path 'gui' failed

我的 .gitmodules 文件看起来像这样:

[submodule "gui"]
        path = gui
        url = ../client/gui.git

这个为什么失败了,我该怎么解决这个问题呢?

你应该先克隆一个裸仓库,或者创建一个裸仓库,将普通仓库的内容推送到裸仓库中,然后再克隆裸仓库。 - J.J. Hakala
@J.J.Hakala 这是一种黑客方式,还是处理 git 本地仓库的惯用方式?它似乎过于复杂了。 - Woodrow Barlow
4个回答

6
问题出在你的.gitmodules文件中。你的项目中的子模块url是从超级项目存储库定义的相对路径,但是当子模块被克隆时,它们会被放置在path位置。
换句话说,git试图从url位置拉取子模块,但在你的本地机器上,它们实际上位于path位置。
要解决这个问题,先只克隆本地超级项目存储库(git clone /path/to/superproject),然后进入新克隆的.gitsubmodules并将urls更改为./。例如,你的gui子模块将变成:
[submodule "gui"]
        path = gui
        url = ./gui

.gitmodules 中的每个子模块都更改为以下内容,然后运行:

git submodule sync
git submodule update --init --recursive

并且这样就可以了!

1
如果我们想要从本地克隆的子模块中克隆子模块,可以使用以下版本。这只允许一个级别的子模块。
git clone $src $dst
modules=$(git -C $src config --file .gitmodules --name-only --get-regexp url)
for module in $modules; do
  module_path=$(git -C $src config --file .gitmodules ${module%.url}.path)
  git -C $dst config ${module} $src/$module_path
done
git -C $dst submodule update --init


1
我需要一个更全面的解决方案,因为Gitlab在克隆文件时有一种奇怪的方式(SRC是顶层源代码库文件夹,DST是请求的顶层目标文件夹):
git clone $SRC $DST
MODULES=$(git -C $SRC config --file .gitmodules --name-only --get-regexp url)
for MODULE in ${MODULES}; do
    MODULE_PATH=$(git -C $SRC config ${MODULE});
    git -C $DST config ${MODULE} ${MODULE_PATH};
done
git -C $DST submodule update --init --recursive;

如果有一种内置的方式来做这件事就太好了...


不错。它会从源代码克隆子模块。我将其用作脚本的基础,以从本地克隆的子模块中克隆子模块。 - Jan Wielemaker
在我的特定情况下,我正在使用gitlab构建环境,因此我想使用实际的url(本地文件夹为空)。我有另一个版本可用,可以使用本地文件夹,这种情况下我使用以下命令:MODULE_PATH=$SRC/$(git -C $SRC config --file=.gitmodules ${MODULE} --get-regexp path),其中我获取的是“--get-regexp path”,而不是url。 - Compholio

0

注意:在Git 2.12或更早版本中,由于子模块的某些不寻常路径,此git submodule update --init --recursive可能会失败。
这在Git 2.13(2017年第二季度)中已得到修复。

请参见cf9e55f提交(2017年4月7日),作者为Brandon Williams(mbrandonw
(由Junio C Hamano -- gitster --5bceab4提交中合并,日期为2017年4月24日)

子模块: 防止子模块名称中的反斜杠扩展

当尝试添加一个名称中带有反斜杠的子模块时,'git submodule' 会以一种有趣的方式失败。我们可以看到一些反斜杠被扩展,导致了一个错误的路径:

git -C main submodule add ../sub\\with\\backslash
fatal: repository '/tmp/test/sub\witackslash' does not exist
fatal: clone of '/tmp/test/sub\witackslash' into submodule path

为了解决这个问题,在git-submodule.sh中将对 'read' 的调用转换为 'read -r',以防止子模块名称中的反斜杠扩展。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接