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

我想在我的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是否真的需要子模块标签中的整个path

 [submodule ".emacs.d/vimpulse"] 

或者也可以只存储子项目的名称?

 [submodule "vimpulse"] 

注意:正如在评论中提到的,这个答案是指旧版本的git所需的步骤。 Git现在支持移动子模块:

既然git 1.8.5, git mv old/submod new/submod按预期工作,并为你做所有的pipe道工作。 您可能想要使用git 1.9.3或更高版本,因为它包含子模块移动修复程序。


这与您如何移除子模块相似(请参阅如何移除子模块? ):

  1. 编辑.gitmodules并适当地更改子模块的path,并使用git add .gitmodules将其放在索引中。
  2. 如果需要,创build子模块的新位置( 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项指向新的位置,所以在这个例子中它应该是worktree = ../../../../../new/parent/module 。 通常应该有两个以上的目录,然后在那个地方的直接path中。
  8. 编辑文件new/parent/module/.git ,确保其中的path指向主项目.git文件夹内的正确的新位置,所以在本例中gitdir: ../../../.git/modules/new/parent/submodule .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. 最后,进行更改。

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

  1. 升级到Git 2.0(或至less1.9.3)
  2. git mv old/submod new/submod
  3. 之后,.gitmodules和submodule目录已经被提交了一个提交(你可以用git status来validation)。
  4. git commit提交更改,而且你还是很好的!

完成!

在我的情况下,我想将一个子目录从一个目录移动到子目录,例如“AFNetworking” – >“ext / AFNetworking”。 这些是我遵循的步骤:

  1. 编辑.gitmodules更改子模块的名称和path为“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 

Et瞧。 上面的例子并没有改变目录深度,这对任务的复杂性有很大的影响,并且不会改变子模块的名字(这可能并不是真的有必要,但是我是这么做的如果我在该path上添加了一个新模块,将会发生。)

[更新:2014-11-26]正如Yar总结的那样, 在你做任何事情之前,请确保你知道子模块的URL。 如果未知,请打开.git/.gitmodules并检查关键submodule.<name>.url

对我而言,使用git submodule deinit <submodule>然后使用git rm <submodule-folder> 删除旧的子 git submodule deinit <submodule> 。 然后再次使用新的文件夹名称添加子模块并提交。 提交之前检查git状态显示旧的子模块重命名为新的名称和.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" 

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

  • 将子模块移到新家。
  • 编辑子模块的工作目录中的.git文件,并修改其包含的path,使其指向主存储库的.git/modules目录中的正确目录。
  • input主存储库的.git/modules目录,并find与您的子模块对应的目录。
  • 编辑config文件,更新工作worktreepath,使其指向子模块工作目录的新位置。
  • 编辑主存储库根目录下的.gitmodules文件,更新子模块工作目录的path。
  • git add -u
  • git add <parent-of-new-submodule-directory> (重要的是添加父项 ,而不是子模块目录本身。)

一些注意事项:

  • .gitmodules.git/config[submodule "submodule-name"]行必须相互匹配,但不对应其他任何内容。
  • 子模块工作目录和.git目录必须正确指向对方。
  • .gitmodules.git/config文件应该同步。

您可以添加一个新的子模块,并使用标准命令删除旧的子模块。 (应该防止.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}" 

大型子模块的奖金方法:

如果子模块很大,而您不希望等待克隆,则可以使用旧的作为原点来创build新的子模块,然后切换原点。

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

 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 ... 

“[submodule]”之后的引号中的string无关紧要。 如果你愿意,你可以把它改成“foobar”。 它用来在“.git / config”中find匹配的条目。

因此,如果您在运行“git submodule init”之前进行更改,则可以正常工作。 如果你进行了更改(或通过合并来获取更改),则需要手动编辑.git / config或再次运行“git submodule init”。 如果你使用后者,你将会在.git / config中留下一个无用的“搁浅”条目,并且保留旧的名字。

给定的解决scheme不适合我,但是类似的版本…

这是与克隆的存储库,因此submodule git回购包含在顶级存储库.git目录。 所有的阳离子都来自顶层库:

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

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

  3. 跑:

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

我想知道是否有所不同,如果存储库是primefaces,或相对的子模块,在我的情况下,它是相对的(submodule / .git是ref to topproject / .git / modules / submodule)

只需使用shell脚本git-submodule-move即可 。

我刚刚经历了这个磨难, 这个答案完美无缺。 这是我的步骤,为了清晰起见:

  1. 确保检查子模块并将其推送到其服务器。 你也需要知道它的分支。
  2. 你需要你的子模块的URL! 使用more .gitmodules因为一旦你删除子模块,它不会在周围
  3. 现在你可以使用deinitrmsubmodule add

  • Git子模块在:Classes / lib / mustIReally
  • 移到:lib / AudioBus
  • url: http : //developer.audiob.us/download/SDK.git

COMMANDS

  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不会这样做。 完全一样。