How do I move an existing Git submodule within a Git repository?
Asked Answered
F

12

531

I would like to change the directory name of a Git submodule in my Git superproject.

Lets suppose I have the following entry in my .gitmodules file:

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

What do I have to type to move the .emacs.d/vimpulse directory to .emacs.d/vendor/vimpulse without deleting it first (explained here and here) and then re-adding it.

Does Git really need the whole path in the submodule tag

[submodule ".emacs.d/vimpulse"]

or is it also possible to store just the name of the subproject?

[submodule "vimpulse"]
Freezing answered 5/1, 2011 at 13:24 Comment(6)
NOTE: the OP answers his/her own question with the git mv command, right in the question.Monostome
HOWEVER, you cannot use git mv like this. Use deinit then rm as specified https://mcmap.net/q/22679/-how-do-i-move-an-existing-git-submodule-within-a-git-repository.Monostome
@Yar: at least on git 2.0.0, git mv just works for submodules also, no need for anything else.Lanchow
Beginning with Git 1.8.5 moving submodules is supported natively using the git mv command (from the release notes, first linked by @Freezing himself). Also answered hereTheir
git mv does move the submodule in the workspace, and update the submodule .git files correctly, but the subfolder within the .git/modules folder of the parent repo stays the same - is that ok? (I'm using git 2.19.0 on Windows)Billiebilling
Suggest simplifying the example some. Using hidden directories in your example adds necessary complexity to the question making less desirable for promotion to me, but it has a great answer and so should be promoted.Thomism
V
622

Newer versions of git

Git now has native support for moving submodules:

Since git 1.8.5, git mv old/submod new/submod works as expected and does all the plumbing for you. You might want to use git 1.9.3 or newer, because it includes fixes for submodule moving.

Older versions of git

As mentioned in the comments, this answer refers to the steps needed with older versions of git.

The process is similar to how you'd remove a submodule (see How do I remove a submodule?):

  1. Edit .gitmodules and change the path of the submodule appropriately, and put it in the index with git add .gitmodules.

  2. If needed, create the parent directory of the new location of the submodule (mkdir -p new/parent).

  3. Move all content from the old to the new directory (mv -vi old/parent/submodule new/parent/submodule).

  4. Make sure Git tracks this directory (git add new/parent).

  5. Remove the old directory with git rm --cached old/parent/submodule.

  6. Move the directory .git/modules/old/parent/submodule with all its content to .git/modules/new/parent/submodule.

  7. Edit the .git/modules/new/parent/config file, make sure that worktree item points to the new locations, so in this example it should be worktree = ../../../../../new/parent/module. Typically there should be two more .. than directories in the direct path in that place.

  8. Edit the file new/parent/module/.git, make sure that the path in it points to the correct new location inside the main project .git folder, so in this example gitdir: ../../../.git/modules/new/parent/submodule.

    git status output looks like this for me afterwards:

     # On branch master
     # Changes to be committed:
     #   (use "git reset HEAD <file>..." to unstage)
     #
     #       modified:   .gitmodules
     #       renamed:    old/parent/submodule -> new/parent/submodule
     #
    
  9. Finally, commit the changes.

Verner answered 10/6, 2011 at 17:53 Comment(20)
after that you need to manually clean up your [submodules] .git/config, right ? Or is there a command which does that?Freezing
Is this still the suggested way to to that?Berkman
FWIW, I was unable to get this to work - perhaps there's a step missing that I'm unaware of?Knuth
When you update .gitmodules make sure you update both that path configuration and the submodule's name. For example, in moving foo/module to bar/module you must change in .gitmodules the section [submodule "foo/module"] to [submodule "bar/module"], and under that same section path = foo/module to path = bar/module. Also, you must change in .git/config the section [submodule "foo/module"] to [submodule "bar/module"].Sidestep
It didn't work for me either... the closest solution I've found is deleting a submodule (a pain) and then re-add it in the different location.Aposematic
Weird enough, following the same instructions but cloning a new repository instead of "moving" the files, I found they work for some submodules. For ie: I'm getting some of them renamed: plugins/foo -> plugins/asd/bar for others, it doesn't "understand" that they're being moved: new file: plugins/another_dir/new and deleted: plugins/old (instead of renamed: plugins/old -> plugins/another_dir/new. I have a lot of submodules, every one changed on .gitmodules. So I don't get why it's acting randomly :SAposematic
A very-very important note: If you get fatal: 'git status --porcelain' failed in... just delete any .git files or directories in the submodule.Whichever
Worked for me, thanks. The only thing is that git add new/parent didn't do anything for me since git doesn't track directories. I has to ` git add new/parent/submodule` instead.Pelting
The order of steps was wrong for me. git add new/parent didn’t do anything after mkdir, which makes sense, because Git can’t track empty directories. I had to git add new/parent after moving the submodule directory with mv.Adulterate
.git/modules/MODULENAME/config needs to be updated to point to the new path.Tew
It looks like this post misses a few steps, such as editing .git/modules/old/parent/submodule, moving it to the new location, updating gitdir in old/parent/submodule/.git...Admissive
Unfortunately this didn't work for me either. I kept getting fatal: Not a git repository: ../.git/modules/foobar, b/c I think it's missing the steps in this answer. I actually don't think it's wise to muck around in Git plumbing without the right tools. That's why I think the best answer is the one that uses Git tools to remove and clone the new submodule.Penult
I hate this answer. Update your git client and use ´git submodule deinit´ evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntuMicra
Or a better way: sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install gitMicra
Since git 1.8.5, git mv old/submod new/submod works as expected and does all the plumbing for you. You probably want to use git 1.9.3+ because it includes fixes for submodule moving.Megalomania
@Megalomania Using 2.2.1 and still have to manually change the name in parent/submodule/.git because it references old name, and remove .git/modules/parent/old. Maybe because I'm also renaming it using git mv parent/old parent/new.Triglyceride
As mentioned in a couple other comments, the solution https://mcmap.net/q/22679/-how-do-i-move-an-existing-git-submodule-within-a-git-repository (bellow) is much betterRecto
Great man! At step 4 i got "fatal: Not a git repository: ../../" so i had to edit old/parent/submodule/.git to reflect the new locationProtagoras
I thought the notation old/submod was confusing. You can use the regular mv syntax. In the example the / indicates that the old location of the submod was in the folder old. The / thus has the regular meaning of directory separatorGammer
When I run git mv old/submod new/submod, I get fatal: renaming 'old/submod' failed: No such file or directory. This is not an enourmously helpful error message, since it led me to believe that Git didn't find old/submod, which was not the case. The problem was that new didn't exist. Adding that folder solved the problem.Cassimere
R
300

The most modern answer, taken from Valloric's comment above:

  1. Upgrade to Git 1.9.3 (or 2.18 if the submodule contains nested submodules)
  2. git mv old/submod new/submod
  3. Afterwards the .gitmodules and the submodule directory are already staged for a commit (you can verify this with git status.)
  4. Commit the changes with git commitand you're good to go!

Done!

Ribera answered 15/7, 2014 at 20:14 Comment(12)
This indeed worked with 1.9.3 except for a submodule inside the moved submodule. That needed some manual cleanup.Battleplane
This should already work in version 1.8.5 as described in the release notes.Their
This answer should get 1000 upvotes, I almost made a mess with my repo doing the steps described above, really StackOverflow should have a usecase for this situation.Bricabrac
Wow, this worked like a charm (git 1.9.5), I wish it was the selected answer.Impugn
This seems solid. git mv old/submod new/submod could definitely be better explained for the masses though.Bengali
Yes I also just used this git mv technique and it worked great.Recto
I can echo what @Battleplane said. I am using version 2.7.0.windows.1Slay
One thing this doesn't do is that it doesn't change the initial label for the submodule. If you check the .gitmodules file, the old/submod is still be used as the label for the submodule while the path has been changed. To get the label changed as well, it appears you need to actually move the modules directory path inside .git, and then manually change the label in .gitmodules.Hatshepsut
Doing this in Git Bash inside VSCode didn't work for me at first (Permission denied error) - likely VSCode locks up the .git files. Running in a separate Git Bash it worked fine.Hypersensitize
git mv worked for me. As the destination folder is empty, I had to invoke an additional git submodule update --init to see the submodules in the destination folder re-appear. They re-appear with same commit hash in the original.Jigger
@eigenfield, a more fool-proof command is git submodule update --init --recursive, in case your submodule contains a submodule, and so-on.Hindorff
@CMCDragonkai, I was unable to manually rename any of those things. No matter what I did I'd end up with git commands, such as git status, returning errors like fatal: not a git repository: path/to/old_submodule_name/../../../.git/modules/path/to/old_submodule_name. If you are able to figure out how to manually rename that stuff after running git mv, please add an answer.Hindorff
Y
57

In my case, I wanted to move a submodule from one directory into a subdirectory, e.g. "AFNetworking" -> "ext/AFNetworking". These are the steps I followed:

  1. Edit .gitmodules changing submodule name and path to be "ext/AFNetworking"
  2. Move submodule's git directory from ".git/modules/AFNetworking" to ".git/modules/ext/AFNetworking"
  3. Move library from "AFNetworking" to "ext/AFNetworking"
  4. Edit ".git/modules/ext/AFNetworking/config" and fix the [core] worktree line. Mine changed from ../../../AFNetworking to ../../../../ext/AFNetworking
  5. Edit "ext/AFNetworking/.git" and fix gitdir. Mine changed from ../.git/modules/AFNetworking to ../../git/modules/ext/AFNetworking
  6. git add .gitmodules
  7. git rm --cached AFNetworking
  8. git submodule add -f <url> ext/AFNetworking

Finally, I saw in the git status:

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 voila. The above example doesn't change the directory depth, which makes a big difference to the complexity of the task, and doesn't change the name of the submodule (which may not really be necessary, but I did it to be consistent with what would happen if I added a new module at that path.)

Yorke answered 28/4, 2012 at 12:22 Comment(3)
Thanks Matt. I was lost on the accepted answer. Thank you for covering more than the base case. This worked like a charm.Uticas
You don't need to shuffle around .git/modules paths, or change the name of the submodule (as arand and Bob Bell mention). Though, doing so may keep things cleaner.Maynord
Don't forget to do steps 2, 3, 4 and 5 recursively for any sub-submodules.Sterne
P
28

[Update: 2014-11-26] As Yar summarizes nicely below, before you do anything, make sure you know the URL of the submodule. If unknown, open .git/.gitmodules and examine the keysubmodule.<name>.url.

What worked for me was to remove the old submodule using git submodule deinit <submodule> followed by git rm <submodule-folder>. Then add the submodule again with the new folder name and commit. Checking git status before committing shows the old submodule renamed to the new name and .gitmodule modified.

$ 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"
Penult answered 19/9, 2013 at 10:39 Comment(4)
This requires git 1.8.3 or higher. See this post for upgrading your git: evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntuMicra
Or, a better way: sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install gitMicra
@MichaelCole Thanks! Right you are! See Git-1.8.3 Release Notes. FYI: Ubuntu-13.10 (Saucy Salamander) has Git-1.8.3.2, but good to know there is ppa. Also, IMHO git subtree merge strategy is a better approach; I have abandoned submodules for my own projects. Still good to understand for existing projects.Penult
I tried several solutions but yours is the best. Only use command line so you don't need (and should not) modify any git file. Thanks!Endblown
B
13

The trick seems to be understanding that the .git directory for submodules are now kept in the master repository, under .git/modules, and each submodule has a .git file that points to it. This is the procedure you need now:

  • Move the submodule to its new home.
  • Edit the .git file in the submodule's working directory, and modify the path it contains so that it points to the right directory in the master repository's .git/modules directory.
  • Enter the master repository's .git/modules directory, and find the directory corresponding to your submodule.
  • Edit the config file, updating the worktree path so that it points to the new location of the submodule's working directory.
  • Edit the .gitmodules file in the root of the master repository, updating the path to the working directory of the submodule.
  • git add -u
  • git add <parent-of-new-submodule-directory> (It's important that you add the parent, and not the submodule directory itself.)

A few notes:

  • The [submodule "submodule-name"] lines in .gitmodules and .git/config must match each other, but don't correspond to anything else.
  • The submodule working directory and .git directory must correctly point to each other.
  • The .gitmodules and .git/config files should be synchronised.
Boomerang answered 14/11, 2012 at 13:16 Comment(0)
S
9

The string in quotes after "[submodule" doesn't matter. You can change it to "foobar" if you want. It's used to find the matching entry in ".git/config".

Therefore, if you make the change before you run "git submodule init", it'll work fine. If you make the change (or pick up the change through a merge), you'll need to either manually edit .git/config or run "git submodule init" again. If you do the latter, you'll be left with a harmless "stranded" entry with the old name in .git/config.

Sealskin answered 5/1, 2011 at 17:47 Comment(2)
This is really annoying, but you're right. The worst part is, if you just change the URL, running git init doesn't seem to update it, you do have to edit .git/config manually.Constrain
in this case git submodule sync propagates the change to .git/config automaticallyElnaelnar
G
9

You can just add a new submodule and remove the old submodule using standard commands. (should prevent any accidental errors inside of .git)

Example setup:

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>

Examle move 'jquery' to '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}"

Bonus method for large submodules:

If the submodule is large and you prefer not to wait for the clone, you can create the new submodule using the old as origin, and then switch the origin.

Example (use same example setup)

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
...
Gowan answered 18/12, 2012 at 22:57 Comment(1)
If you're not using head, you may also need to check out the correct version of the module at newPath.Gag
F
3

Just use the shell script git-submodule-move.

Footlambert answered 22/7, 2013 at 12:20 Comment(1)
Heh, I looked up this question again, and used one of the higher voted answers, and now I wish that I'd scrolled down and seen my previous answer which I'd forgotten about.Footlambert
L
2

The given solution did not work for me, however a similar version did...

This is with a cloned repository, hence the submodule git repos are contained in the top repositories .git dir. All cations are from the top repository:

  1. Edit .gitmodules and change the "path =" setting for the submodule in question. (No need to change the label, nor to add this file to index.)

  2. Edit .git/modules/name/config and change the "worktree =" setting for the submodule in question

  3. run:

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

I wonder if it makes a difference if the repositories are atomic, or relative submodules, in my case it was relative (submodule/.git is a ref back to topproject/.git/modules/submodule)

Lumbago answered 25/4, 2012 at 20:0 Comment(0)
M
2

I just went through this ordeal yesterday and this answer worked perfectly. Here are my steps, for clarity:

  1. Ensure that submodule is checked in and pushed to its server. You also need to know what branch its on.
  2. You need the URL of your submodule! Use more .gitmodules because once you delete the submodule it's not going to be around
  3. Now you can use deinit, rm and then submodule add

EXAMPLE

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 

NOTE: git mv doesn't do this. At all.

Monostome answered 15/4, 2014 at 15:5 Comment(2)
Good summary. +1 git mv should be better in the very last versions of Git though.Strong
@Strong I tested on git 1.8.5, pretty sure that's about as good as it gets for mv. Thanks!Monostome
M
1

For me running git mv <old/path> <new/path failed with:

Rename from 'old/path' to 'new/path' failed. Should I try again? (y/n)

After running git deinit <old/path> I was able to use git mv.

Marmoreal answered 13/7, 2022 at 6:58 Comment(0)
E
-1

I meet the same problem and solve it successfully. Thanks for this Github issue delete_git_submodule.md

To remove a submodule you need to:

  • Delete the relevant section from the .gitmodules file.
  • Stage the .gitmodules changes, git add .gitmodules
  • Delete the relevant section from .git/config. (maybe doesn't exists)
  • Run git rm --cached path_to_submodule (no trailing slash).
  • Run rm -rf .git/modules/path_to_submodule (no trailing slash).
  • Commit git commit -m "Removed submodule "
  • Delete the now untracked submodule files rm -rf path_to_submodule
Electrocorticogram answered 27/1, 2022 at 3:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.