508

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"]
Community
  • 1
  • 1
thisch
  • 5,261
  • 3
  • 16
  • 14
  • NOTE: the OP answers his/her own question with the `git mv` command, right in the question. – Dan Rosenstark Apr 15 '14 at 00:02
  • HOWEVER, you cannot use `git mv` like this. Use `deinit` then `rm` as specified http://stackoverflow.com/a/18892438/8047. – Dan Rosenstark Apr 15 '14 at 14:56
  • 19
    @Yar: at least on git 2.0.0, ``git mv`` _just works_ for submodules also, no need for anything else. – Pedro Romano Jun 26 '14 at 07:55
  • 11
    Beginning with Git `1.8.5` moving submodules is supported natively using the `git mv` command ([from the release notes](https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.8.5.txt), first linked by @thisch himself). Also [answered here](http://stackoverflow.com/a/24767348/1928529) – Dennis van der Schagt Nov 29 '14 at 13:43
  • 1
    `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) – yoyo Jan 14 '20 at 18:28
  • 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. – brookbot Jul 14 '22 at 22:30

12 Answers12

587

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.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
Axel Beckert
  • 6,814
  • 1
  • 22
  • 23
  • after that you need to manually clean up your [submodules] .git/config, right ? Or is there a command which does that? – thisch Jun 12 '11 at 12:15
  • Is this still the suggested way to to that? – Drake Guan Jul 11 '11 at 06:57
  • FWIW, I was unable to get this to work - perhaps there's a step missing that I'm unaware of? – Allen George Aug 09 '11 at 22:07
  • 41
    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"]`. – wilhelmtell Nov 12 '11 at 14:11
  • 3
    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. – Pablo Olmos de Aguilera C. Dec 06 '11 at 01:29
  • 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 :S – Pablo Olmos de Aguilera C. Dec 06 '11 at 23:15
  • 35
    A very-very important note: If you get `fatal: 'git status --porcelain' failed in...` just delete any .git files or directories in the submodule. – antitoxic Mar 11 '12 at 09:14
  • 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. – Savageman Mar 30 '12 at 13:03
  • 2
    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`. – Rory O'Kane Aug 27 '12 at 19:46
  • 1
    `.git/modules/MODULENAME/config` needs to be updated to point to the new path. – Erik Kaplun Oct 14 '12 at 09:57
  • 19
    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`... – szx Mar 28 '13 at 13:48
  • 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](http://stackoverflow.com/a/10363449/1020470). 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](http://stackoverflow.com/a/13942859/1020470) is the one that uses Git tools to remove and clone the new submodule. – Mark Mikofski Sep 19 '13 at 10:32
  • 1
    I hate this answer. Update your git client and use ´git submodule deinit´ http://evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntu/ – Michael Cole Feb 07 '14 at 13:36
  • Or a better way: sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install git – Michael Cole Feb 07 '14 at 13:45
  • 41
    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. – Valloric May 14 '14 at 00:05
  • 1
    @Valloric 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`. – Karsten S. Mar 27 '15 at 13:32
  • As mentioned in a couple other comments, the solution http://stackoverflow.com/a/24767348/262379 (bellow) is much better – Dinis Cruz Jun 08 '15 at 08:08
  • 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 location – Indio Jun 09 '15 at 08:31
  • 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 separator – Nino van Hooff Apr 06 '16 at 07:31
295

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!

dbosk
  • 3
  • 2
phatmann
  • 18,161
  • 7
  • 61
  • 51
  • 3
    This indeed worked with `1.9.3` _except_ for a submodule inside the moved submodule. That needed some manual cleanup. – Pascal Sep 05 '14 at 14:10
  • 3
    This should already work in version `1.8.5` as described in [the release notes](https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.8.5.txt). – Dennis van der Schagt Nov 28 '14 at 17:07
  • 7
    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. – MGP Mar 13 '15 at 17:01
  • 5
    Wow, this worked like a charm (git 1.9.5), I wish it was the selected answer. – Alex Ilyaev Mar 22 '15 at 11:52
  • 2
    This seems solid. `git mv old/submod new/submod` could definitely be better explained for the masses though. – sheriffderek Apr 24 '15 at 06:14
  • 1
    Yes I also just used this ```git mv``` technique and it worked great. – Dinis Cruz Jun 08 '15 at 08:08
  • 1
    I can echo what @Pascal said. I am using version 2.7.0.windows.1 – Gregory Kuhn Jan 10 '16 at 18:02
  • 15
    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`. – CMCDragonkai Jan 07 '17 at 11:12
  • 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. – Toivo Säwén Jan 30 '20 at 13:58
  • 1
    `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. – daparic Jun 05 '20 at 09:17
  • @eigenfield, a more fool-proof command is `git submodule update --init --recursive`, in case your submodule contains a submodule, and so-on. – Gabriel Staples Mar 25 '22 at 14:41
  • @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. – Gabriel Staples Mar 26 '22 at 19:01
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.)

Matt Connolly
  • 9,757
  • 2
  • 65
  • 61
  • 4
    Thanks Matt. I was lost on the accepted answer. Thank you for covering more than the base case. This worked like a charm. – Andrew Hubbs Jan 22 '13 at 00:19
  • 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. – gatoatigrado Mar 25 '13 at 21:51
  • Don't forget to do steps 2, 3, 4 and 5 recursively for any sub-submodules. – herzbube Oct 05 '13 at 18:33
27

[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"
Community
  • 1
  • 1
Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
  • 1
    This requires git 1.8.3 or higher. See this post for upgrading your git: http://evgeny-goldin.com/blog/3-ways-install-git-linux-ubuntu/ – Michael Cole Feb 07 '14 at 13:35
  • 1
    Or, a better way: sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install git – Michael Cole Feb 07 '14 at 13:43
  • @MichaelCole Thanks! Right you are! See [Git-1.8.3 Release Notes](https://github.com/git/git/blob/v1.8.3/Documentation/RelNotes/1.8.3.txt#L148). FYI: [Ubuntu-13.10 (Saucy Salamander) has Git-1.8.3.2](http://packages.ubuntu.com/saucy/git), but good to know there is [ppa](https://launchpad.net/~git-core/+archive/ppa). Also, IMHO [git subtree merge strategy](http://git-scm.com/book/ch6-7.html) is a better approach; I have abandoned submodules for my own projects. Still good to understand for existing projects. – Mark Mikofski Feb 07 '14 at 19:51
  • 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! – nahung89 Jul 31 '19 at 00:41
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.
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
...
Lance Rushing
  • 7,540
  • 4
  • 29
  • 34
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.

Bob Bell
  • 363
  • 2
  • 5
  • 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. – crimson_penguin Jan 06 '12 at 20:09
  • 1
    in this case `git submodule sync` propagates the change to `.git/config` automatically – CharlesB Apr 25 '12 at 20:12
3

Just use the shell script git-submodule-move.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • 2
    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. – Flimm May 19 '14 at 14:07
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)

arand
  • 154
  • 1
  • 8
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.

Community
  • 1
  • 1
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
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.

tnix
  • 31
  • 3
-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
Armstrongya
  • 795
  • 1
  • 6
  • 9