如何在Git存储库中移动现有的Git子模块?


356

我想在我的Git超级项目中更改Git子模块的目录名称。

假设我的.gitmodules文件中包含以下条目:

[submodule ".emacs.d/vimpulse"]  
path = .emacs.d/vimpulse  
url = git://gitorious.org/vimpulse/vimpulse.git

我必须键入什么才能将.emacs.d/vimpulse目录移动到.emacs.d/vendor/vimpulse而不先删除它(在这里这里说明 ),然后重新添加它。

Git是否真的需要子模块标签中的整个路径

[submodule ".emacs.d/vimpulse"]

还是也可以只存储子项目的名称?

[submodule "vimpulse"]

注意:OP用git mv命令回答该问题,就在问题中。
Dan Rosenstark 2014年

但是,您不能这样使用git mvdeinit然后使用rm 指定的stackoverflow.com/a/18892438/8047
Dan Rosenstark 2014年

14
@Yar:至少在git 2.0.0上,也git mv 仅适用于子模块,不需要其他任何东西。
Pedro Romano 2014年

9
从Git开始,1.8.5使用git mv命令本身就支持移动子模块(从发行说明开始,首先由@thisch自己链接)。在这里
dennisschagt 2014年

git mv会在工作区中移动子模块并正确更新子模块.git文件,但是父仓库的.git / modules文件夹中的子文件夹保持不变-可以吗?(我在Windows上使用git 2.19.0)
yoyo

Answers:


377

注意:如评论中所述,此答案指的是旧版本git所需的步骤。Git现在具有移动子模块的本机支持:

从git 1.8.5开始,git mv old/submod new/submod可以按预期工作,并为您完成所有工作。您可能要使用git 1.9.3或更高版本,因为它包含用于子模块移动的修复程序。


该过程类似于您删除子模块的方式(请参阅如何删除子模块?):

  1. .gitmodules适当地编辑和更改子模块的路径,然后使用将其放在索引中git add .gitmodules
  2. 如果需要,请在子模块(mkdir -p new/parent)的新位置创建父目录。
  3. 将所有内容从旧目录移动到新目录(mv -vi old/parent/submodule new/parent/submodule)。
  4. 确保Git跟踪此目录(git add new/parent)。
  5. 使用删除旧目录git rm --cached old/parent/submodule
  6. 将目录.git/modules/old/parent/submodule及其所有内容移到.git/modules/new/parent/submodule
  7. 编辑.git/modules/new/parent/config文件,确保工作树项指向新位置,因此在此示例中,它应为worktree = ../../../../../new/parent/module。通常,..在该位置的直接路径中,目录应该比目录多两个。
  8. 编辑文件new/parent/module/.git,确保其中的路径指向主项目.git文件夹中正确的新位置,因此在此示例中gitdir: ../../../.git/modules/new/parent/submodule

    git status 之后的输出对我来说是这样的:

    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       modified:   .gitmodules
    #       renamed:    old/parent/submodule -> new/parent/submodule
    #
    
  9. 最后,提交更改。


37
更新.gitmodules时,请确保同时更新该path配置和子模块的名称。例如,将foo / module移到bar / module时,必须在.gitmodules中将section更改[submodule "foo/module"][submodule "bar/module"],并将同一节path = foo/module更改为path = bar/module。另外,您必须在.git / config中将部分更改[submodule "foo/module"][submodule "bar/module"]
wilhelmtell

3
它对我也不起作用...我找到的最接近的解决方案是删除一个子模块(痛苦),然后将其重新添加到其他位置。
Pablo Olmos de Aguilera C.

33
一个非常重要的注意事项:如果fatal: 'git status --porcelain' failed in...只删除子模块中的任何.git文件或目录。
抗毒剂2012年

19
看来此帖子错过了一些步骤,例如编辑.git/modules/old/parent/submodule,将其移至新位置,gitdirold/parent/submodule/.git...中进行更新
szx

38
从git 1.8.5开始,git mv old/submod new/submod可以按预期工作,并为您完成所有工作。您可能要使用git 1.9.3+,因为它包含用于子模块移动的修复程序。
Valloric

232

最现代的答案,取自Valloric的上述评论:

  1. 升级到Git 1.9.3(如果子模块包含嵌套子模块,则升级为2.18
  2. git mv old/submod new/submod
  3. 之后,.gitmodules和submodule目录已经准备好提交(您可以使用进行验证git status)。
  4. 提交更改,git commit您就可以开始了!

做完了!


3
1.9.3 除了在移动的子模块中的子模块之外,这确实可以使用。这需要一些手动清理。
Pascal 2014年

3
这应该已经可以在发行说明中1.8.5描述的版本中使用
dennisschagt

6
这个答案应该得到1000个投票,按照上述步骤,我几乎把我的仓库弄得一团糟,实际上StackOverflow应该有一个用例来解决这种情况。
MGP

5
哇,这就像一个魅力(git 1.9.5),我希望它是选定的答案。
Alex Ilyaev 2015年

7
这不做的一件事是它不会更改子模块的初始标签。如果您检查.gitmodules文件,old/submod则在更改路径后,仍将用作子模块的标签。要同时更改标签,您似乎需要在其中移动模块目录路径.git,然后在中手动更改标签.gitmodules
CMCDragonkai

55

就我而言,我想将一个子模块从一个目录移动到一个子目录,例如“ AFNetworking”->“ ext / AFNetworking”。这些是我遵循的步骤:

  1. 编辑.gitmodules,将子模块名称和路径更改为“ ext / AFNetworking”
  2. 将子模块的git目录从“ .git / modules / AFNetworking”移至“ .git / modules / ext / AFNetworking”
  3. 将库从“ AFNetworking”移动到“ ext / AFNetworking”
  4. 编辑“ .git / modules / ext / AFNetworking / config”并修复该[core] worktree行。我的从../../../AFNetworking改为../../../../ext/AFNetworking
  5. 编辑“ ext / AFNetworking / .git”并修复gitdir。我的从../.git/modules/AFNetworking改为../../git/modules/ext/AFNetworking
  6. git add .gitmodules
  7. git rm --cached AFNetworking
  8. git submodule add -f <url> ext/AFNetworking

最后,我看到了git状态:

matt$ git status
# On branch ios-master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   .gitmodules
#   renamed:    AFNetworking -> ext/AFNetworking

等等。上面的示例没有更改目录深度,这对任务的复杂性有很大的影响,并且没有更改子模块的名称(这也许不是必需的,但我这样做是为了与如果我在该路径上添加了新模块,就会发生这种情况。)


4
谢谢马特。我迷失了公认的答案。多谢您介绍基本案例。这就像一个魅力。
Andrew Hubbs 2013年

您不需要在.git / modules路径周围改组,也不需要更改子模块的名称(就像arand和Bob Bell提到的那样)。但是,这样做可以使环境更清洁。
gatoatigrado

不要忘记对任何子子模块递归执行步骤2、3、4和5。
herzbube

22

[更新:2014-11-26]正如Yar在下面很好地总结的那样,在执行任何操作之前,请确保您知道子模块的URL。如果未知,请打开.git/.gitmodules并检查钥匙submodule.<name>.url

对我有用的是使用,git submodule deinit <submodule>然后使用删除旧的子模块git rm <submodule-folder>。然后使用新的文件夹名称再次添加子模块并提交。在提交之前检查git status会显示将旧子模块重命名为新名称,并修改.gitmodule。

$ git submodule deinit foo
$ git rm foo
$ git submodule add https://bar.com/foo.git new-foo
$ git status
renamed:    foo -> new-foo
modified:   .gitmodules
$ git commit -am "rename foo submodule to new-foo"

1
这需要git 1.8.3或更高版本。请参阅此帖子以升级您的git:evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntu
Michael Cole

1
或者,更好的方法:sudo add-apt-repository ppa:git-core / ppa sudo apt-get更新sudo apt-get安装git
Michael Cole

@MichaelCole谢谢!你是对的!参见Git-1.8.3发行说明。仅供参考:Ubuntu-13.10(Saucy Salamander)具有Git-1.8.3.2,但很高兴知道有ppa。同样,恕我直言git子树合并策略是一种更好的方法。我已经为自己的项目放弃了子模块。对于现有项目仍然很容易理解。
Mark Mikofski 2014年

我尝试了几种解决方案,但是您的解决方案是最好的。仅使用命令行,因此您不需要(也不应该)修改任何git文件。谢谢!
nahung89 '19

12

技巧似乎是在了解.git子模块的目录现在保留在主存储库中的下.git/modules,并且每个子模块都有一个.git指向它的文件。这是您现在需要的过程:

  • 将子模块移到其新位置。
  • .git在子模块的工作目录中编辑文件,然后修改其包含的路径,使其指向主存储库目录中的正确.git/modules目录。
  • 输入主存储库的.git/modules目录,然后找到与您的子模块相对应的目录。
  • 编辑config文件,更新worktree路径,使其指向子模块工作目录的新位置。
  • 编辑.gitmodules主存储库根目录中的文件,将路径更新到子模块的工作目录。
  • git add -u
  • git add <parent-of-new-submodule-directory>(重要的是添加parent,而不是子模块目录本身。)

一些注意事项:

  • [submodule "submodule-name"]以行.gitmodules.git/config必须相互匹配,但不符合任何东西。
  • 子模块的工作目录和.git目录必须正确指向彼此。
  • .gitmodules.git/config文件应该是同步的。

9

“ [submodule””后面的引号中的字符串无关紧要。您可以根据需要将其更改为“ foobar”。它用于在“ .git / config”中找到匹配的条目。

因此,如果在运行“ git submodule init”之前进行了更改,它将可以正常工作。如果进行更改(或通过合并进行更改),则需要手动编辑.git / config或再次运行“ git submodule init”。如果执行后者,则将留下一个无害的“绞线”条目,其旧名称位于.git / config中。


这确实很烦人,但是你是对的。最糟糕的是,如果仅更改URL,则运行git init似乎无法更新它,则必须手动编辑.git / config。
crimson_penguin 2012年

1
在这种情况下git submodule sync,更改会.git/config自动传播
CharlesB 2012年

9

您可以仅使用标准命令添加新的子模块并删除旧的子模块。(应防止.git内部出现任何意外错误)

设置示例:

mkdir foo; cd foo; git init; 
echo "readme" > README.md; git add README.md; git commit -m "First"
## add submodule
git submodule add git://github.com/jquery/jquery.git
git commit -m "Added jquery"
## </setup example>

例如将“ jquery”移动到“ vendor / jquery / jquery”:

oldPath="jquery"
newPath="vendor/jquery/jquery"
orginUrl=`git config --local --get submodule.${oldPath}.url`

## add new submodule
mkdir -p `dirname "${newPath}"`
git submodule add -- "${orginUrl}" "${newPath}"

## remove old submodule
git config -f .git/config --remove-section "submodule.${oldPath}"
git config -f .gitmodules --remove-section "submodule.${oldPath}"
git rm --cached "${oldPath}"
rm -rf "${oldPath}"              ## remove old src
rm -rf ".git/modules/${oldPath}" ## cleanup gitdir (housekeeping)

## commit
git add .gitmodules
git commit -m "Renamed ${oldPath} to ${newPath}"

大型子模块的奖励方法:

如果子模块很大,并且您不想等待克隆,则可以使用旧的作为原点创建新的子模块,然后切换原点。

示例(使用相同的示例设置)

oldPath="jquery"
newPath="vendor/jquery/jquery"
baseDir=`pwd`
orginUrl=`git config --local --get submodule.${oldPath}.url`

# add new submodule using old submodule as origin
mkdir -p `dirname "${newPath}"`
git submodule add -- "file://${baseDir}/${oldPath}" "${newPath}"

## change origin back to original
git config -f .gitmodules submodule."${newPath}".url "${orginUrl}"
git submodule sync -- "${newPath}"

## remove old submodule
...

如果您没有使用head,则可能还需要在检出模块的正确版本newPath
paulmelnikow

2

给定的解决方案对我不起作用,但是类似的版本却适用...

这是一个克隆的存储库,因此子模块git repos包含在顶级存储库.git dir中。所有阳离子均来自顶部的存储库:

  1. 编辑.gitmodules并更改相关子模块的“ path =”设置。(无需更改标签,也无需将此文件添加到索引。)

  2. 编辑.git / modules / name / config并更改所涉及子模块的“ worktree =”设置

  3. 跑:

    mv submodule newpath/submodule
    git add -u
    git add newpath/submodule
    

我想知道存储库是原子库还是相对子模块是否会有所不同,在我的情况下,它是相对的(submodule / .git是对topproject / .git / modules / submodule的引用)



2

我昨天刚刚经历了这个磨难,这个答案非常有效。为了清楚起见,这是我的步骤:

  1. 确保已检入子模块并将其推送到其服务器。您还需要知道它的分支。
  2. 您需要子模块的URL!使用more .gitmodules是因为一旦删除子模块,它将不再存在
  3. 现在您可以使用deinitrm然后submodule add

指令

    git submodule deinit Classes/lib/mustIReally
    git rm foo
    git submodule add http://developer.audiob.us/download/SDK.git lib/AudioBus

    # do your normal commit and push
    git commit -a 

注意: git mv不会这样做。完全没有


3
好总结。git mv不过,在最新版本的Git中,+ 1 应该更好。
VonC 2014年

@VonC我在git 1.8.5上进行了测试,非常确定这与它的性能差不多mv。谢谢!
Dan Rosenstark 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.