23

Initially I had the following (simplified) repo structure:

MyRepo
   external1/MySub (git submodule)
   .gitsubmodules

Where

$ cat .gitsubmodules

  [submodule "external1/MySub"]
  path = external1/MySub
  url = user@repo:/remoterepo/externals/MySub.git

I then wanted to move MySubmodule to another directory in the same git repository, e.g., external2 so the structure is as follows:

MyRepo
   external2/MySub (git submodule)
   .gitsubmodules

What I did, and thought is enough, I simply moved (via OS mv) the external1/MySub directory to external2/MySub and edited the .gitsubmodules file to:

$ cat .gitsubmodules

  [submodule "external2/MySub"]
  path = external2/MySub
  url = user@repo:/remoterepo/externals/MySub.git

After this change, I get the following error:

$ git status

  fatal: Could not chdir to '../../../../../repo/external/MySub': No such file or directory
  fatal: 'git status --porcelain' failed in submodule repo/external2/MySub

What am I missing? Any other changes need to be done for such move to work?

(I'm using git version 1.8.3rc3 on Windows 8.1)

Jacek M
  • 2,349
  • 5
  • 22
  • 47
  • Possible duplicate of [How do I move an existing Git submodule within a Git repository?](https://stackoverflow.com/questions/4604486/how-do-i-move-an-existing-git-submodule-within-a-git-repository) – Rich Nov 01 '17 at 14:36

2 Answers2

35

I simply moved the external1/MySub directory to external2/MySub

Move as in mv (unix command), or git mv (git command)?

You need to move the special entry of the index representing the submodule for the parent repo.

Normally, (reading git mv), this should have been enough:

git mv external1/MySub external2/MySub

Moving a submodule using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will update the gitfile and core.worktree setting to make the submodule work in the new location.
It also will attempt to update the submodule.<name>.path setting in the gitmodules file and stage that file (unless -n is used).

That works best with the latest 1.8.5 git (like msysgit for Windows, or latest package for Unix)


Suseika adds in the comments

If you get message

fatal: renaming '%submodule%' failed: No such file or directory

it is probably because you're adding a directory level, e.g. moving submodule "math" to "libs/math".
git mv doesn't create missing [intermediate] directories, you should mkdir them yourself.


git 2.9 (June 2016) will improve git mv for submodule:

See commit a127331 (19 Apr 2016) by Stefan Beller (stefanbeller).
(Merged by Junio C Hamano -- gitster -- in commit 9cb50a3, 29 Apr 2016)

mv: allow moving nested submodules

"git mv old new" did not adjust the path for a submodule that lives as a subdirectory inside old/ directory correctly.

submodules however need to update their link to the git directory as well as updates to the .gitmodules file.


Git 2.12 (Q1 2017) offers to move nested submodules to the parent repo:

There is a new submodule helper "git submodule absorbgitdirs" to make it easier to move embedded .git/ directory for submodules in a superproject to .git/modules/ (and point the latter with the former that is turned into a "gitdir:" file) has been added.

See commit 7c4be45 (27 Dec 2016), commit f6f8586, commit 47e83eb, commit 1a248cf (12 Dec 2016), and commit 6f94351, commit 89c8626, commit 90c0011, commit 6f94351, commit 89c8626, commit 90c0011 (08 Dec 2016) by Stefan Beller (stefanbeller).
(Merged by Junio C Hamano -- gitster -- in commit da2b74e, 10 Jan 2017)

submodule: add absorb-git-dir function

When a submodule has its git dir inside the working dir, the submodule support for checkout that we plan to add in a later patch will fail.

Add functionality to migrate the git directory to be absorbed into the superprojects git directory.

The newly added code in this patch is structured such that other areas of Git can also make use of it. The code in the submodule--helper is a mere wrapper and option parser for the function absorb_git_dir_into_superproject, that takes care of embedding the submodules git directory into the superprojects git dir. That function makes use of the more abstract function for this use case relocate_gitdir, which can be used by e.g. the worktree code eventually to move around a git directory.


Note: there is still (Git 2.14.x/2.15, Q4 2017) a bug linked to submodule moving: see "Git: moving submodules recursively (nested submodules)".


Git 2.15.x/2.16 (Q1 2018) will make moving submodule more robust, since "git fetch --recurse-submodules" now knows that submodules can be moved around in the superproject in addition to getting updated, and finds the ones that need to be fetched accordingly.

See commit 4b4aced, commit c68f837 (16 Oct 2017), and commit 01ce122 (06 Oct 2017) by Heiko Voigt (hvoigt).
(Merged by Junio C Hamano -- gitster -- in commit b4d658b, 06 Nov 2017)

implement fetching of moved submodules

We store the changed submodules paths to calculate which submodule needs fetching. This does not work for moved submodules since their paths do not stay the same in case of a moved submodules.
In case of new submodules we do not have a path in the current checkout, since they just appeared in this fetch.

It is more general to collect the submodule names for changes instead of their paths to include the above cases. If we do not have a configuration for a gitlink we rely on constructing a default name from the path if a git repository can be found at its path. We skip non-configured gitlinks whose default name collides with a configured one.


Note that, before Git 2.19 (Q3 2018), the code to try seeing if a fetch is necessary in a submodule during a fetch with --recurse-submodules got confused when the path to the submodule was changed in the range of commits in the superproject, sometimes showing "(null)".
This has been corrected.

See commit c3749f6, commit 5fc8475 (14 Jun 2018) by Stefan Beller (stefanbeller).
(Merged by Junio C Hamano -- gitster -- in commit 085d2ab, 28 Jun 2018)

submodule: fix NULL correctness in renamed broken submodules

When fetching with recursing into submodules, the fetch logic inspects the superproject which submodules actually need to be fetched.
This is tricky for submodules that were renamed in the fetched range of commits. This was implemented in c68f837 (implement fetching of moved submodules, 2017-10-16, Git v2.16.0), and this patch fixes a mistake in the logic there.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    Reverted all my changes and trying to git mv I'm getting: $ git mv external/MySub external2/MySub fatal: source directory is empty, source=external/MySub, destination=external/MySub (even though the source dir is not empty at all) – Jacek M Jan 08 '14 at 09:15
  • @jaccus You need to reset your repo as it was before your `mv`. Then do the `git mv` (without modifying the `.gitmodules`) and see if it works. – VonC Jan 08 '14 at 09:16
  • I think that's what I'm running into while doing `git mv` on a submodule http://noone.org/blog/English/Computer/VCS/How%20to%20move%20a%20git%20submodule.html – Jacek M Jan 08 '14 at 09:22
  • 1
    @jaccus only if you have an old git. Make sure to update to the latest one first (https://launchpad.net/~git-core/+archive/ppa + https://launchpad.net/+help-soyuz/ppa-sources-list.html), then reset, then `git mv`, and it will work. – VonC Jan 08 '14 at 09:33
  • I'll check that later on the latest git version (although mine's not that old /added to the question description/). I managed to make it work with the hacks I posted in another answer. I hope it's not too dangerous and won't break something. – Jacek M Jan 08 '14 at 09:51
  • @jaccus git1.8.3rc3 is old (May 2013!) I would advise unzipping an 1.8.5.2 (https://code.google.com/p/msysgit/downloads/list?can=3&q=portable&colspec=Filename+Summary+Uploaded+ReleaseDate+Size+DownloadCount) and test with that (you can keep both git version installed at the same time) – VonC Jan 08 '14 at 09:53
  • So `git mv` on a submodule worked like a charm with git 1.8.5.2. Should have updated git in the first place. Thanks! – Jacek M Jan 08 '14 at 10:06
  • @jaccus great! I have edited the answer to reflet that git version pre-requisite. – VonC Jan 08 '14 at 10:08
  • I had a folder containing 2 submodules and moved that folder one level deeper using `git mv myfolder/ subfolder/` and I had to correct `gitdir` and `worktree`entries for these folders manually under cygwin git 2.1.1. Tortoise git in Windows Explorer does not show the submodules' status. – Bernhard Döbler Oct 21 '14 at 22:15
  • 1
    @Bardware I prefer cloning again the parent repo, and use `git mv myfolder` (no trailing `/`): you want to move the **gitlink entry** (http://stackoverflow.com/a/16581096/6309), not the folder. – VonC Oct 22 '14 at 05:17
  • 4
    If you get message "fatal: renaming '%submodule%' failed: No such file or directory", it is probably because you're adding a directory level, e.g. moving submodule "math" to "libs/math". `git mv` doesn't create missing directories, you should `mkdir` them yourself. – gvlasov Feb 09 '16 at 16:58
  • 1
    @Suseika Good point. I have included it in the answer for more visibility. – VonC Feb 10 '16 at 08:53
18

I changed a few more things manually and it actually worked with git 1.8.3rc3 (unnecessary with git 1.8.5):

  1. Fix gitdir path in .git submodule file repo/external2/MySub/.git:

    -gitdir: ../../../.git/modules/external1/MySub
    +gitdir: ../../../.git/modules/external2/MySub
    
  2. Rename submodule directory under repo/.git/modules/ directory

    $ mv repo/.git/modules/external1 repo/.git/modules/external2
    
  3. Fix worktree path in repo/.git/modules/external2/MySub/config

    -worktree = ../../../../../external1/MySub
    +worktree = ../../../../../external2/MySub
    
Jacek M
  • 2,349
  • 5
  • 22
  • 47