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

thi*_*sch 331 git git-submodules

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

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

[submodule ".emacs.d/vimpulse"]  
path = .emacs.d/vimpulse  
url = git://gitorious.org/vimpulse/vimpulse.git
Run Code Online (Sandbox Code Playgroud)

我必须键入什么来移动.emacs.d/vimpulse目录.emacs.d/vendor/vimpulse而不首先删除它(在这里这里解释 ),然后重新添加它.

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

[submodule ".emacs.d/vimpulse"]
Run Code Online (Sandbox Code Playgroud)

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

[submodule "vimpulse"]
Run Code Online (Sandbox Code Playgroud)

Axe*_*ert 339

注意:正如评论中所提到的,这个答案指的是旧版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项指向新位置,因此在此示例中应该是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
    #
    
    Run Code Online (Sandbox Code Playgroud)
  9. 最后,提交更改.

  • 更新.gitmodules时,请确保更新`path`配置和子模块的名称.例如,在将foo/module移动到bar /模块时,必须将.gitmodules中的`[submodule"foo/module"]`更改为``submodule"bar/module"]`,并在同一节``path = foo/module`到`path = bar/module`.此外,您必须将.git/config中的`[submodule"foo/module"]`更改为`[submodule"bar/module"]`. (37认同)
  • 从git 1.8.5开始,`git mv old/submod new/submod`按预期工作,并为您完成所有管道工作.您可能想使用git 1.9.3+,因为它包含子模块移动的修复程序. (36认同)
  • 一个非常重要的注意事项:如果你得到'致命:'git status --porcelain'失败了......`只需删除子模块中的任何.git文件或目录. (32认同)
  • 看起来这篇文章错过了几个步骤,例如编辑`.git/modules/old/parent/submodule`,将其移动到新位置,更新`old/parent/submodule/.git`中的`gitdir`. . (19认同)
  • 它对我来说也不起作用......我发现最接近的解决方案是删除一个子模块(一个痛苦),然后在不同的位置重新添加它. (3认同)
  • 步骤的顺序对我来说是错误的.`gt add new/parent`在`mkdir`之后没有做任何事情,这是有道理的,因为Git无法跟踪空目录.在使用`mv`移动子模块目录后,我不得不`git add new/parent`*. (2认同)

pha*_*ann 222

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

  1. 升级到Git 1.9.3(如果子模块包含嵌套的子模块,则升级到2.18)
  2. git mv old/submod new/submod
  3. 之后,.gitmodules和子模块目录已经为提交暂存(您可以使用git status.进行验证.)
  4. 承诺改变,git commit你很高兴!

完成!

  • 这个答案应该得到1000个upvotes,我差点弄乱我的repo做上面描述的步骤,真的StackOverflow应该有这种情况的用例. (6认同)
  • 哇,这就像一个魅力(git 1.9.5),我希望它是选定的答案. (5认同)
  • 这样做的一件事是它不会改变子模块的初始标签.如果检查`.gitmodules`文件,那么当路径被更改时,`old/submod`仍然被用作子模块的标签.为了更改标签,您似乎需要实际移动`.git`中的模块目录路径,然后手动更改`.gitmodules`中的标签. (5认同)
  • 对于移动子模块内的子模块,这确实与`1.9.3` _except_一起使用.这需要一些手动清理. (3认同)
  • 这应该已经在版本`1.8.5中工作,如[发行说明](https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.8.5.txt)中所述. (3认同)
  • 这看起来很可靠。不过,“git mv old/submod new/submod”绝对可以更好地为大众解释。 (2认同)

Mat*_*lly 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
Run Code Online (Sandbox Code Playgroud)

瞧瞧.上面的例子不会改变目录深度,这会对任务的复杂性产生很大的影响,并且不会改变子模块的名称(这可能不是必需的,但我这样做是为了与如果我在那条路径上添加了一个新模块,就会发生.)

  • 谢谢马特.我对接受的答案感到迷茫.感谢您覆盖超过基本案例.这就像一个魅力. (4认同)

Mar*_*ski 20

[更新:2014-11-26]由于Yar在下面做了很好的总结,在你做任何事情之前,请确保你知道子模块的URL.如果未知,请打开.git/.gitmodules并检查密钥submodule.<name>.url.

对我有用的删除旧的子模块,git submodule deinit <submodule>然后使用git rm <submodule-folder>.然后使用新文件夹名称和提交再次添加子模块.在提交之前检查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"
Run Code Online (Sandbox Code Playgroud)


小智 11

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

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

几点说明:

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


Lan*_*ing 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>
Run Code Online (Sandbox Code Playgroud)

检查将'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}"
Run Code Online (Sandbox Code Playgroud)

大子模块的奖励方法:

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

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

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
...
Run Code Online (Sandbox Code Playgroud)


Bob*_*ell 8

"[子模块"]之后引号中的字符串无关紧要.如果需要,您可以将其更改为"foobar".它用于在".git/config"中查找匹配的条目.

因此,如果您在运行"git submodule init"之前进行了更改,它将正常工作.如果您进行更改(或通过合并获取更改),则需要手动编辑.git/config或再次运行"git submodule init".如果您执行后者,您将留下一个无害的"搁浅"条目,其中包含.git/config中的旧名称.