如何使用 `git submodule add` 命令添加已存在的子仓库?

28

问题

如何将已有的子仓库作为 Git 子模块添加?

原因

我有一个私有的 codespace 超级模块,其中子模块随意分散:

codespace (git repo, private)
├── Archived_projects (git repos)
└── Projects
    ├── project-foo (git repo)
    └── project-bar (git repo)

有时子模块可能存在未准备好推送的提交。但是我希望在推送超级模块codespace时能够保存它们。
codespace是克隆到c9.io工作区或其他地方的仓库。

我的做法

linus@machine /cygdrive/f/__Storage__/Workspace
$ git clone https://github.com/octocat/Spoon-Knife.git
Cloning into 'Spoon-Knife'...
$ cd Spoon-Knife/
$ git clone https://github.com/octocat/Spoon-Knife.git ./foo/bar
Cloning into './foo/bar'...
$ git add .

来自 cmd.exe

> git submodule add https://github.com/octocat/Spoon-Knife.git ./foo/bar
'foo/bar' already exists in the index
> cat .gitmodules
cat: .gitmodules: No such file or directory

cygwin.exe (bash) 中

$ git submodule add https://github.com/octocat/Spoon-Knife.git ./foo/bar
': not a valid identifier/Git/mingw64/bin/gettext.sh: line 89: export: `sm_path
'' already exists in the index
$ cat .gitmodules
cat: .gitmodules: No such file or directory

参考资料

git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
              [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]

<repository> is the URL of the new submodule’s origin repository.

<path> is the relative location for the cloned submodule to exist in the superproject. If <path> does not exist, then the
submodule is created by cloning from the named URL. If <path> does exist and is already a valid Git repository, then this is
added to the changeset without cloning. This second form is provided to ease creating a new submodule from scratch, and
presumes the user will later push the submodule to the given URL.

In either case, the given URL is recorded into .gitmodules for use by subsequent users cloning the superproject. If the URL
is given relative to the superproject’s repository, the presumption is the superproject and submodule repositories will be
kept together in the same relative location, and only the superproject’s URL needs to be provided: git-submodule will
correctly locate the submodule using the relative URL in .gitmodules.

如果 <path> 存在并且已经是一个有效的 Git 仓库,则将其添加到变更集中而无需克隆。

为什么这在我的情况下不起作用?


这可能会有所帮助。将repo移出您的工作树,添加子模块,修复URL。 - jthill
8个回答

33

出了什么问题

你所犯的错误在于做了...

$ git add .

这会将所有内容添加到当前存储库的索引中(因此可以进行提交),包括foo/bar

正确的方法

如果您不这样做并继续进行如下操作:

$ git submodule add https://github.com/CarloWood/XYZ.git foo/bar

那么这应该有效; 这将检测到foo / bar已经克隆的存储库,并将其作为子模块添加到当前存储库中。

请注意,不需要先克隆。您明确表示您已经完成了这项工作,但出于其他读者的清晰度,我想指出,如果在 git add . 之前省略克隆(因此现在根本没有foo / bar),则上面的 git submodule add ... 会看到还没有任何东西,然后会自动为您克隆。

请注意,方法之间存在轻微差异。如果您首先进行克隆,则 foo / .git 将是一个目录,而如果使用 git submodule add 进行克隆,则将此 .git 存储库放置在父项目的 .git / modules / foo 中,而 foo / .git 是一个包含该路径的文件。然而,实际上没有区别,因为使用文件将 .git 指向任何其他位置都是通用的,并且可以在任何地方使用; 您无法从文件或目录中的 .git 得出任何结论。


谢谢。我在5年后添加了它,所以他们不再监视它并不奇怪:D。也许@ilyaigpetrov在此期间已经去世了! - Carlo Wood

10

一种方法是手动创建.gitmodules文件。

格式如下:

[submodule "path/to/submodule1"]
    path = path/to/submodule/1
    url = git@github.com:user/submodule1
[submodule "path/to/submodule2"]
    path = path/to/submodule/2
    url = git@github.com:user/submodule2

请注意,您需要运行。
git submodule sync

应用更改


1
注意,这是 .gitmodules - Tom
很遗憾,这似乎没有任何作用。 - DustWolf
1
@DustWolf 你需要运行 git submodule sync 命令来应用更改。 - Rufus
@Rufus 我没有尝试过,但听起来可能会。我最终使用了这个解决方案:https://dev59.com/GHNA5IYBdhLWcg3wjOve#30675130 ...这似乎更安全。 - DustWolf

7

git submodule add 命令会检测给定子模块路径是否存在且包含已初始化的 git 仓库,所以不需要担心这个问题。我曾遇到类似的问题,因此编写了一个(有点 hack 的)脚本来处理这个问题。

#!/bin/bash
# save super directory
currentDir=`pwd`
# get all the git repos inside (except .) and format them 
# the way git does
gitDirs=`find -type d -name ".git" | sed -e 's|.git$||' -e 's|./||' -e 's|/$||' | grep -v "^$"`

for i in ${gitDirs[@]}
do
        echo "dealing with $i now"

        cd $i 
        # get the remote url for each submodule
        fetchUrl=`git remote -v | awk '/fetch/ {print $2}'`
        # for my purposes repos without remotes are useless
        # but you may have a different use case
        if [[ -z $fetchUrl ]]
        then
                echo "fetch url not found for this directory"
                continue
        else                                                      
                echo "got a fetch url of $fetchUrl for git repo $i"                                                                 
        fi                                                        

        cd $currentDir
        # make sure it isn't tracked as a submodule already                                                         
        existing=`grep -A5  $i ./.gitmodules | grep $fetchUrl`    

        if [[ -z $existing ]]                                     
        then                                                      
                echo "does not exist in .gitmodules yet, will create now"
                # if it doesn't exist yet then create it
                git submodule add $fetchUrl $i
        else
                echo "$i is already present as a submodule with fetch url: $fetchUrl"
                echo "The command we would have used is: git submodule add $fetchUrl $i"
        fi
done


4
不要使用 git add 来添加子模块,而是使用git submodule add
git submodule [--quiet] add [<options>] [--] <repository> [<path>]

如果您已经克隆了存储库,只需运行
git submodule add -- "$(git -C ./sub/ remote get-url origin)" ./sub/
NB:相对路径前缀./在此处非常重要...除非您正在指定绝对路径。

或者

git submodule add -- ./sub.git ./sub

如果项目使用相对URL。


需要注意的是,这将添加一个子模块,它实际上指向本地工作副本,而不是远程副本。您可以通过查看更新后的.gitmodules文件来自行验证。 - Sridhar Ratnakumar
是的,本地存储库已添加为存储库。如果想在事后更改它,请使用“git submodule set-url --sub "$(git -C ./sub/ remote get-url origin)"”更改此配置。 - CervEd

3
为了补充Carlos的回答,对于一个已经存在的仓库,它已经包含了它的URL。你可以简单地:
 git submodule add $(cat repodir/.git/config  | grep url | cut -d '=' -f 2) reponame

2
也许你可以使用git config来读取url,而不是自己解析配置文件:git submodule add $(git -C repodir config remote.$(git -C repodir config branch.master.remote).url) repodir。这会选择主分支的远程跟踪分支的url。(即使存在多个远程,此方法也适用,你提供的示例在这种情况下无法工作。)(git -C somedirectory表示git的行为就像从somedirectory中启动一样。) - T S
不必要的复杂,git -C sub/ remote get-url origin | xargs -I% git submodule add % ./sub/ - CervEd
更加简单的方法是 git submodule add ./sub/,注意 ./ - CervEd

2
  1. 从索引中删除文件 foo/bar: git rm -f --cached foo/bar
  2. git submodule add https://github.com/octocat/Spoon-Knife.git ./foo/bar

0

至少如果(未来的)子模块没有递归其他repositories/.git目录,那么以下命令将简单地添加所有子模块。(我不确定为什么我必须将目录作为路径和“repository”提供给命令,但无论如何...)

for d in $(dirname $(find -mindepth 2 -name .git)) ; do git submodule add -- ./$d/ ./$d/ ; done

-3

您无需手动克隆。在运行 git submodule add [url] [path] 之后,运行 git submodule update,它会为您克隆/拉取所有子模块。


我已经有一个克隆和修改数据的存储库了。理想情况下,我希望能够将子模块与超级模块一起推送到服务器上。如果不可能的话,我接受你的答案。 - ilyaigpetrov
我有同样的问题,已经克隆并修改了一些文件,如何将这个修改后的仓库作为子模块添加到另一个git仓库中? - Chinglin Wen

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