如何将一个已经在子目录中检出的嵌套 repo 作为子模块添加到父 Git repo 中?

7
如果我从我的工作(父)目录创建初始提交,但有包含独立检出的Git仓库的子目录,那会发生什么?
我只是运行了git add .,但这让我陷入了一个奇怪的情况,即具有嵌套Git仓库的子目录未被注册为父仓库的子模块。
那么:在父工作目录中使用“git add .”后如何处理包含独立检出的嵌套git仓库的子目录(以获取正确的子模块)?
例如:
[imz@z super-existing-sub]$ ls 
c  sub
[imz@z super-existing-sub]$ ls -a sub/
.  ..  a  .git
[imz@z super-existing-sub]$ 

所以,在 super-existing-sub 中已经存在一个预设的 super-existing-sub/sub git 存储库。
当我在 super-existing-sub 中运行时:
$ git init
$ git add .

如何正确地将现有的Git仓库注册为子模块?

现在,Git已经以某种方式跟踪它:

$ git status 
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   c
    new file:   sub

$ 

但是git submodule存在一些问题:

$ git submodule status 
No submodule mapping found in .gitmodules for path 'sub'
$ 

如何将它转换为正确的子模块?


我尝试按照答案所建议的方式进行操作(由Victor和我组成),即git submodule add URL subdir,但不幸的是,这种方法会导致问题:

$ git submodule status
No submodule mapping found in .gitmodules for path 'wp-content/themes/liquorice'
$ git submodule add git@github.com:/nudgeme/Liquorice.git ./wp-content/themes/liquorice
'wp-content/themes/liquorice' already exists in the index
/sshx:kosmoplus:/home/kosmoplus/kosmoplus.ru.old $ git submodule status
No submodule mapping found in .gitmodules for path 'wp-content/themes/liquorice'
$ 

嵌套GIT库的陷阱!(好吧,这是一个GIT测验)中,这里有一个类似的历史:他们在子目录下使用Git仓库时,没有使用git submodule add或任何其他git submodule命令,而是直接运行了git add . - imz -- Ivan Zakharyaschev
3个回答

7

在维克多·约翰逊的回答基础上,这里提供一个bash一行命令来一次性添加大量子模块。

find . -name config | grep git/config | grep -v ProjectToExclude | grep -v AnotherProjectToExclude | xargs -n 1 -I % sh -c 'cat % | grep -m 1 url | sed "s/url = //g"; echo % | sed "s/.git\/config//g";' | xargs -n 2 echo git submodule add

详细说明

假设您有一个包含许多git存储库的文件夹,并且您希望父存储库将它们全部作为子模块:

path/to/one_repo
path/to/one_repo/.git/config
path/to/another_repo
path/to/another_repo/.git/config
(etc.)

您需要对每个子模块运行git submodule add ./path/to/submodule/命令。

1. 这个一行代码会为每个可能的子模块打印该命令。 以干冷方式运行它以查看哪些文件夹具有包含url=.git/config文件。请注意,它假定第一个出现的url是远程的 - 请确保对结果进行合理性检查。

这个一行代码的解释如下:

find . -name config |                            # find all config files
    grep git/config |                            # include only those that have "git/config" in their path
    grep -v ProjectToExclude |                   # exclude config files with ProjectToExclude in their path
    grep -v AnotherProjectToExclude |            # ditto
    xargs -n 1 -I % sh -c '                      # one line at a time, execute the following commands, substituting the path for `%`
        cat % |                                  # print the config file contents
            grep -m 1 url |                      # take the first line that contains `url`
            sed "s/url = //g";                   # remove the "url = " part
        echo % |                                 # print the path to the config file
            sed "s/.git\/config//g";             # remove '.git/config' to keep only the project path
    ' |                                          # summary: print the remote url on every 2nd line; the path on every 2nd line 
    xargs -n 2 echo git submodule add            # take two lines at a time, print them with the command

请注意,您可以将任意数量的grep -v ProjectToExclude添加到管道中以排除这些文件夹-再次运行oneliner以查看要执行的新命令列表,并注意ProjectToExclude不再出现。
这将打印输出:
git submodule add https://github.com/username/one_repo.git path/to/one_repo
git submodule add git@otherdomain.com:another_repo.git path/to/another_repo
(etc.)

2. 然后,实际执行:删除命令末尾的 echo,这样最后一部分就改为

xargs -n 2 echo git submodule add

为了

xargs -n 2 git submodule add

(我们不再是输出命令,而是直接执行它。)

3. 添加到git。 现在你可以运行 git status 命令来查看你刚刚做了什么。


非常感谢 - 这个一行代码连同解释帮了很大的忙! - Erik Brendel

3

我一直使用 git submodule init 命令让git识别它们,然后使用 git submodule update 命令将子模块克隆到工作目录中。但这不是本次提问的情况。

要从父目录创建一个新的git repo,你需要在父目录上运行 git init 命令,然后再运行 git submodule add ./path/to/submodule/ 命令来添加子模块。

注意: 子模块路径必须是绝对路径,所以你应该在路径前加上 ./。

或者,如果你想要一个良好的外部URL作为子模块的URL,你需要先在 ./path/to/submodule/.git/config 中查找原始URL,然后就能够进行操作。

git submodule add URL ./path/to/submodule/

我还没有在实践中尝试过这个,但是git-submodule的manpage说:

如果


1
如果子目录中已经有一个Git仓库怎么办?(因此我不必克隆子模块,因为它已经存在。然后我也想将包含目录作为Git仓库,并带有子模块。) - imz -- Ivan Zakharyaschev
那么,如果子目录中已经有一个已检出的Git repo,并且.gitmodules中还没有条目,那么git submodule init将无法工作,对吗? - imz -- Ivan Zakharyaschev
关于第二个建议(git submodule add):这似乎是我在手册中错过的内容。确实:“如果<path>存在且已经是有效的Git存储库,则将其添加到更改集而无需克隆。提供了第二种形式以便从头开始轻松创建新的子模块,并假定用户稍后将推送子模块到给定的URL。” 对我来说,唯一剩下的部分可能是获取子仓库的“origin”,并将其作为git submodule add的第一个参数。之后,我的子模块必须是正确的。 - imz -- Ivan Zakharyaschev
对于你的第一个问题:不,我认为如果子模块已经被检出,git submodule init 不会做任何事情,但你仍然需要运行 git submodule add 告诉 git 它的存在。对于你的第二个评论:如果你是指将新的子模块(来自 origin)添加到存储库中,则只需在你想要放置子模块的目录中运行 git submodule add http://url/to/submodule 即可。 - Victor Johnson
我指的是:当存在一个包含Git仓库的子目录时,我应该查看该子目录中的原始URL(),然后可能运行git submodule add URL subdir来实现我的目标:将已经检出的现有仓库作为子模块添加到父级Git仓库中。 - imz -- Ivan Zakharyaschev
我尝试按照我们提供的答案中建议的方式进行,即 git submodule add URL subdir,但不幸的是它出了问题(详见更新后的问题描述)。 - imz -- Ivan Zakharyaschev

2
请看结论(TL;DR)。
仔细阅读git submodule <URL> <path>的手册,注意以下备注:
如果<path>已经存在并且已经是一个有效的Git存储库,则将其添加到更改集中而无需克隆。 这第二种形式是为了方便从头开始创建新子模块,并假定用户稍后将推送子模块到给定的URL。
让我们在我们这样的情况下尝试使用它(在您将父目录递归添加到新父存储库之后,已经存在嵌套的Git存储库)。
首先,查找./path/to/submodule/.git/config中的原始URL,以获取有关子模块的元信息的漂亮的真实外部URL;其次,调用git submodule <URL> <path>(根据手册的说法,希望它能够完成我们想要的操作)。
让我们来试试:
$ git submodule status
No submodule mapping found in .gitmodules for path 'wp-content/themes/liquorice'
$ git submodule add git@github.com:/nudgeme/Liquorice.git ./wp-content/themes/liquorice
'wp-content/themes/liquorice' already exists in the index
/sshx:kosmoplus:/home/kosmoplus/kosmoplus.ru.old $ git submodule status
No submodule mapping found in .gitmodules for path 'wp-content/themes/liquorice'
$ 

很不幸,它出现了故障。

好的,让我们思考如何解决“已经存在于索引中”的问题,但并非我们想要的那个问题……

……我们可以从索引中简单地删除它!这样就可以成功解决问题:

$ git submodule status
No submodule mapping found in .gitmodules for path 'wp-content/themes/liquorice'
$ git rm --cached ./wp-content/themes/liquorice
rm 'wp-content/themes/liquorice'
$ git submodule add git@github.com:/nudgeme/Liquorice.git ./wp-content/themes/liquorice
Adding existing repo at 'wp-content/themes/liquorice' to the index
$ git submodule status
 9733c0ab3e4207352e3f51d612f2a1c9c4a0b63a wp-content/themes/liquorice (liquorice2.1-1-g9733c0a)
$ 

TL;DR

  • 在子目录中查找原始URL;
  • git rm --cached subdir
  • git submodule add URL subdir

这甚至可以成为一个简单的脚本,以自动化将现有的嵌套存储库作为子模块添加到父级中,但我不认为增加东西很有意义。在这个水平上,只需学习基本的Git命令,然后使用和组合它们!


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