{"thread":{"id":"63398","subject":"Bug Report: git submodule overwrites submodules of same name but different path","startedAt":"2025-05-05T13:41:09Z","lastAt":"2025-05-06T17:30:17Z","messageCount":2,"participants":["mlell08@gmail.com","K Jayatheerth"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"517246","messageId":"5b8e60ba-b05b-4389-8c06-88c143ceee10@gmail.com","threadId":"63398","inReplyTo":null,"subject":"Bug Report: git submodule overwrites submodules of same name but different path","fromName":"","fromEmail":"mlell08@gmail.com","sentAt":"2025-05-05T13:41:06Z","receivedAt":"2025-05-05T13:41:09Z","isPatch":false,"sender":{"key":"mlell08@gmail.com","avatar":null},"body":"Dear list,\n\nI have encountered a problem with git submodule add that leads to info \nof a submodule being lost if another submodule of the same name is \nadded. This can happen if a submodule has existed on the path in an \nearlier commit but has been moved (git mv edits path= in .gitmodules but \nnot the submodule name, probably to avoid moving the corresponding \nfolder in .git/modules)\n\nFind below the summary from \"git bugreport\" (Answers in English).\n\nBest wishes,\nMoritz\n\n\n\n# What did you do to reproduce the error\n\n\ngit init parent\ncd parent\ngit commit --allow-empty -m \"initial commit\"\ngit init child\ngit -C child commit --allow-empty -m \"initial commit\"\ngit submodule add https://example.com/child.git\tchild\ngit commit -m \"Add submodule child\"\n\ngit mv child child_old\ngit commit -m \"Move child to child_old\"\n\ngit init child\ngit -C child commit --allow-empty -m \"initial commit\"\ngit submodule add https://example.com/child2.git child\ngit commit -m \"Add an new submodule at child/\"\n\ncat .gitmodules\n\n\n# What did you expect to happen\n\nI expect that both submodules have entries in the .gitmodules file.\nGit submodule names are given by the path, so I expected some way\nof resolving ambiguity, for example, git appends \".path\" in other\ncases to disambiguate. So the expected .gitmodules content would\nbe\n\n[submodule \"child\"]\n\tpath = child_old\n\turl = https://example.com/child.git\n[submodule \"child.path\"]\n\tpath = child\n\turl = https://example.com/child2.git\n\n# What happened instead\n\nI see that there is only one entry in .gitmodules listing the\nnew submodule, under the name \"child\":\n\n[submodule \"child\"]\n\tpath = child\n\turl = https://example.com/child2.git\n\n# How is this different from the expected result\n\nThe old submodule (path = child_old) is not listed, the information \nabout the origin of the old subrepo is lost.\n\n\n\n[System Info]\ngit Version:\ngit version 2.49.0\ncpu: x86_64\nbuilt from commit: 683c54c999c301c2cd6f715c411407c413b1d84e\nsizeof-long: 8\nsizeof-size_t: 8\nshell-path: /bin/sh\nlibcurl: 8.12.1\nOpenSSL: OpenSSL 3.4.1 11 Feb 2025\nzlib: 1.3.1\nuname: Linux 6.14.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Sun, 20 Apr 2025 \n12:38:52 +0000 x86_64\nCompiler Info: gnuc: 14.2\nlibc Info: glibc: 2.41\n$SHELL (typically, interactive shell): /bin/bash\n\n\n[Active Hooks]\n\n"},{"id":"517370","messageId":"20250506173012.92414-1-jayatheerthkulkarni2005@gmail.com","threadId":"63398","inReplyTo":"5b8e60ba-b05b-4389-8c06-88c143ceee10@gmail.com","subject":"Re \"Bug Report: git submodule overwrites submodules of same name but different path\"","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2025-05-06T17:30:11Z","receivedAt":"2025-05-06T17:30:17Z","isPatch":false,"sender":{"key":"jayatheerthkulkarni2005@gmail.com","avatar":"https://avatars.githubusercontent.com/u/148841023?v=4"},"body":"\n> Dear list,  \n>  \n\nHey Moritz,\nThanks for the report\n\n> I have encountered a problem with `git submodule add` that leads to info  \n> of a submodule being lost if another submodule of the same name is  \n> added. This can happen if a submodule has existed on the path in an  \n> earlier commit but has been moved (`git mv` edits `path=` in `.gitmodules` but  \n> not the submodule name, probably to avoid moving the corresponding  \n> folder in `.git/modules`)  \n>  \n> Find below the summary from \"git bugreport\" (Answers in English).  \n>  \n> Best wishes,  \n> Moritz  \n>  \n>  \n> # What did you do to reproduce the error  \n>  \n>\n> git init parent  \n> cd parent  \n> git commit --allow-empty -m \"initial commit\"  \n> git init child  \n> git -C child commit --allow-empty -m \"initial commit\"  \n> git submodule add https://example.com/child.git child  \n> git commit -m \"Add submodule child\"  \n>  \n> git mv child child_old  \n> git commit -m \"Move child to child_old\"  \n>  \n> git init child  \n> git -C child commit --allow-empty -m \"initial commit\"  \n> git submodule add https://example.com/child2.git child  \n> git commit -m \"Add a new submodule at child/\"  \n>  \n> cat .gitmodules  \n> \n>  \n\nI could reproduce the behaviour, neat observation!!\n\n> # What did you expect to happen  \n>  \n> I expect that both submodules have entries in the `.gitmodules` file.  \n> Git submodule names are given by the path, so I expected some way  \n> of resolving ambiguity, for example, Git appends \".path\" in other  \n> cases to disambiguate. So the expected `.gitmodules` content would  \n> be:  \n>  \n> \n> [submodule \"child\"]  \n>     path = child_old  \n>     url = https://example.com/child.git  \n> [submodule \"child.path\"]  \n>     path = child  \n>     url = https://example.com/child2.git  \n> \n>  \n> # What happened instead  \n>  \n> I see that there is only one entry in `.gitmodules` listing the  \n> new submodule, under the name \"child\":  \n>  \n>  \n> [submodule \"child\"]  \n>     path = child  \n>     url = https://example.com/child2.git  \n> \n>  \n> # How is this different from the expected result  \n>  \n> The old submodule (path = `child_old`) is not listed. The information  \n> about the origin of the old subrepo is lost.  \n>  \n>  \n\nI think the main issue is as follows \nin the module_add() function, \nadd_data.sm_name defaults to the basename of the path you’re\nadding, it re‑uses the already‑existing name “child” even though the\nprevious entry now points at child_old/.\n\nNothing in module_add() checks whether that name is already taken for a\ndifferent path, so the new write silently overwrites the old one.\n\n\nThe original code is something like this \n\nif (!add_data.sm_name)\n\tadd_data.sm_name = add_data.sm_path;\n\nif (check_submodule_name(add_data.sm_name))\n\tdie(_(\"'%s' is not a valid submodule name\"), add_data.sm_name);\n\n\nSomething like this in between works for me\n\nconst struct submodule *existing =\n\t\t\tsubmodule_from_name(the_repository,\n\t\t\t\t\tnull_oid(the_hash_algo),\n\t\t\t\t\tadd_data.sm_name);\n\t\n\t\t\tif (existing && strcmp(existing->path, add_data.sm_path)) {\n\t\t\t\tif (!force) {\n\t\t\t\t\tdie(_(\"submodule name '%s' already used for path '%s' \"\n\t\t\t\t\t      \"(use --name to choose another or --force to overwrite)\"),\n\t\t\t\t\t    add_data.sm_name, existing->path);\n\t\t\t\t}\n\t\t\t\t/* auto-disambiguate: append \".<basename>\" */\n\t\t\t\tadd_data.sm_name = xstrfmt(\"%s.%s\",\n\t\t\t\t\t\t\t   add_data.sm_name,\n\t\t\t\t\t\t\t   basename(add_data.sm_path));\n                        }\n\n\nThis is just a vague format. Just to compiler and check.\n\nWith this the behaviour now throws a fatal error and not change the \nexisting submodules.\n\nSomething like this:\nfatal: submodule name 'child' already used for path 'child_old' (use --name to choose another or --force to overwrite)\nOn branch master\nUntracked files:\n  (use \"git add <file>...\" to include in what will be committed)\n        child/\n\nnothing added to commit but untracked files present (use \"git add\" to track)\n\nIf this is good \nI can send a patch covering the patch with this specific fix and the test file\nI don't think this needs a new test file, I will add in any existing one.\n\nIf anyone has a better idea I'm open to it too.\n\nThank You,\n\n-Jayatheerth\n"}]}