如何使用子模块克隆本地存储库?

How do I clone a local repo with submodules?

假设我递归地克隆了一个存储库。

$ 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

为什么会失败,我该如何解决这个问题?

问题出在您的 .gitmodules 文件上。您项目中的子模块 url 被定义为超级项目存储库的相对路径,但是当子模块被克隆时,它们被放置在 path 位置。

换句话说,git 试图从 url 位置提取子模块,但在您的本地计算机上,它们实际上位于 path 位置。

要解决此问题,请仅克隆本地超级项目存储库 (git clone /path/to/superproject),然后进入新克隆的 .gitsubmodules 并将 url 更改为 ./<whatever-the-path-is> .例如,您的 gui 子模块将变为:

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

.gitmodules中的每个子模块改成这样,然后运行:

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

应该就可以了!

我需要一个比这更全面的解决方案,因为 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;

如果有一个内置的方法来做到这一点就好了...

注意:对于 Git 2.12 或更低版本,此 git submodule update --init --recursive 可能会因为子模块的某些异常路径而失败。
此问题已在 Git 2.13(2017 年第二季度)

中修复

参见 commit cf9e55f (07 Apr 2017) by Brandon Williams (mbrandonw)
(由 Junio C Hamano -- gitster -- in commit 5bceab4 合并,2017 年 4 月 24 日)

submodule: prevent backslash expansion in submodule names

When attempting to add a submodule with backslashes in its name 'git submodule' fails in a funny way. We can see that some of the backslashes are expanded resulting in a bogus path:

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

To solve this, convert calls to 'read' to 'read -r' in git-submodule.sh in order to prevent backslash expansion in submodule names.

如果我们要从本地克隆的子模块中克隆子模块,我来下面这个版本。这只允许一级子模块。

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