gitgit-submodules

git submodule deinit not working as expected after submodule url change


git clone --no-checkout myreop
cd myrepo
git checkout branch001
git submodule update --init
# all good
cd ..; mv myrepo myrepo.backup

git clone --no-checkout myrepo
git checkout branch002
git submodule update --init
# all good

All good even though the submodule repo address changed between branch001 and branch002. I can expect this to work because .git/config has been deleted by cloning branch002 into an empty directory. But, alternatively, I could also have deleted the submodule entries in .git/config with the git submodule deinit command as follows (it does more but I dont think this is relevant here):

git clone --no-checkout myreop
cd myrepo
git checkout branch001
git submodule update --init
git checkout branch002 
# the .gitmodules file now has the correct changed submodule url but the
# .git/config file has the previous one which is not in sync with .gitmodules

git submodule deinit -f --all
# .gitmodules is unchanged, still correct.
# .git/config submodule entries are now deleted as I expected, which is good

git submodule update --init
# .gitmodules still unchanged and correct
# .git/config is now in sync with .gitmodules again

But surprisingly the last command fails nonetheless:

fatal: git upload-pack: not our ref 11ba32ee3c7104f9ce614a393b02624ce09bdcda
fatal: remote error: upload-pack: not our ref 11ba32ee3c7104f9ce614a393b02624ce09bdcda
fatal: Fetched in submodule path 'modulpath', but it did not contain 11ba32ee3c7104f9ce614a393b02624ce09bdcda. Direct fetching of that commit failed.

Looks like the old_submodule_repo is still stored somewhere. So I make a check:

$ find . -type f -exec grep -l "new_submodule_repo" {} \;
./.git/logs/HEAD
./.git/logs/refs/heads/master
./.git/logs/refs/remotes/origin/HEAD
./.git/config
./.gitlab-ci.yml
./.gitmodules

$ find . -type f -exec grep -l "old_submodule_repo" {} \;
# nothing

Ok, I found the solution: git submodule sync instead of git submodule deinit.

But I still wonder where git has stored the old submodule url and why the submodule origin was pointing there even though .git/config had the correct url for origin.

When I say git set-url origin ... then I expect this to be reflected in the .git/config file and honored. If I make the change to the .git/config file directly I also expect this to be honored. And it is honored in both cases. But with submodules things seem not to be the same.

So my questions are:


Solution

  • Looks like the old_submodule_repo is still stored somewhere. So I make a check:

    $ find . -type f -exec grep -l "new_submodule_repo" {} \;
    ./.git/logs/HEAD 
    ./.git/logs/refs/heads/master
    ./.git/logs/refs/remotes/origin/HEAD 
    ./.git/config 
    ./.gitlab-ci.yml
    ./.gitmodules
    
    $ find . -type f -exec grep -l "old_submodule_repo" {} \;
    # nothing
    

    The second command should have found ./.git/modules/modulpath/config since this is where the remote.origin.url config for the submodule remote is ultimately stored. I don't know why your invocation does not find it (it works for me). Maybe you have a non-standard setup ?

    Ok, I found the solution: git submodule sync instead of git submodule deinit.

    Indeed, git submodule sync will copy the submodule URL from .gitmodules to submodule.modulpath.url in .git/config and then to remote.origin.url in .git/modules/modulpath/config.

    But I still wonder where git has stored the old submodule url and why the submodule origin was pointing there even though .git/config had the correct url for origin.

    It was stored in the config file of the submodule repository, as mentioned above. remote.origin.url in the submodule config file is where the URL used to communicate with the submodule remote is taken from.

    Why did git submodule deinit not reset the submodule url regardless the fact that git submodule sync does the job more conveniently without deleting the whole submodule folder?

    git submodule deinit does nothing more than removing the submodule section(s) from the .git/config of the superproject, as documented in 1.

    Where was the old submodule url stored? Why had this "secret" store precedence over .git/config?

    As mentioned above, the URL is stored in the config file of the submodule repository. It is not "secret", a submodule is just a regular Git repository inside another Git repository, it makes sense that the same configuration used to configure a remote URL (remote.origin.url) is also used in submodules.

    Is it a bug in git that git submodule deinit -f --all does not delete the upload pack of the submodule?

    "delete the upload pack of the submodule" does not make much sense, upload-pack is a Git command invoked by git fetch. It is not a bug that git submodule deinit does not touch the config file of the submodule, it just touches the config file of the superproject.