git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Bug Report: git submodule overwrites submodules of same name but different path
@ 2025-05-05 13:41 mlell08
  2025-05-06 17:30 ` Re "Bug Report: git submodule overwrites submodules of same name but different path" K Jayatheerth
  0 siblings, 1 reply; 2+ messages in thread
From: mlell08 @ 2025-05-05 13:41 UTC (permalink / raw)
  To: git

Dear list,

I have encountered a problem with git submodule add that leads to info 
of a submodule being lost if another submodule of the same name is 
added. This can happen if a submodule has existed on the path in an 
earlier commit but has been moved (git mv edits path= in .gitmodules but 
not the submodule name, probably to avoid moving the corresponding 
folder in .git/modules)

Find below the summary from "git bugreport" (Answers in English).

Best wishes,
Moritz



# What did you do to reproduce the error


git init parent
cd parent
git commit --allow-empty -m "initial commit"
git init child
git -C child commit --allow-empty -m "initial commit"
git submodule add https://example.com/child.git	child
git commit -m "Add submodule child"

git mv child child_old
git commit -m "Move child to child_old"

git init child
git -C child commit --allow-empty -m "initial commit"
git submodule add https://example.com/child2.git child
git commit -m "Add an new submodule at child/"

cat .gitmodules


# What did you expect to happen

I expect that both submodules have entries in the .gitmodules file.
Git submodule names are given by the path, so I expected some way
of resolving ambiguity, for example, git appends ".path" in other
cases to disambiguate. So the expected .gitmodules content would
be

[submodule "child"]
	path = child_old
	url = https://example.com/child.git
[submodule "child.path"]
	path = child
	url = https://example.com/child2.git

# What happened instead

I see that there is only one entry in .gitmodules listing the
new submodule, under the name "child":

[submodule "child"]
	path = child
	url = https://example.com/child2.git

# How is this different from the expected result

The old submodule (path = child_old) is not listed, the information 
about the origin of the old subrepo is lost.



[System Info]
git Version:
git version 2.49.0
cpu: x86_64
built from commit: 683c54c999c301c2cd6f715c411407c413b1d84e
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
libcurl: 8.12.1
OpenSSL: OpenSSL 3.4.1 11 Feb 2025
zlib: 1.3.1
uname: Linux 6.14.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Sun, 20 Apr 2025 
12:38:52 +0000 x86_64
Compiler Info: gnuc: 14.2
libc Info: glibc: 2.41
$SHELL (typically, interactive shell): /bin/bash


[Active Hooks]


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re "Bug Report: git submodule overwrites submodules of same name but different path"
  2025-05-05 13:41 Bug Report: git submodule overwrites submodules of same name but different path mlell08
@ 2025-05-06 17:30 ` K Jayatheerth
  0 siblings, 0 replies; 2+ messages in thread
From: K Jayatheerth @ 2025-05-06 17:30 UTC (permalink / raw)
  To: mlell08; +Cc: git


> Dear list,  
>  

Hey Moritz,
Thanks for the report

> I have encountered a problem with `git submodule add` that leads to info  
> of a submodule being lost if another submodule of the same name is  
> added. This can happen if a submodule has existed on the path in an  
> earlier commit but has been moved (`git mv` edits `path=` in `.gitmodules` but  
> not the submodule name, probably to avoid moving the corresponding  
> folder in `.git/modules`)  
>  
> Find below the summary from "git bugreport" (Answers in English).  
>  
> Best wishes,  
> Moritz  
>  
>  
> # What did you do to reproduce the error  
>  
>
> git init parent  
> cd parent  
> git commit --allow-empty -m "initial commit"  
> git init child  
> git -C child commit --allow-empty -m "initial commit"  
> git submodule add https://example.com/child.git child  
> git commit -m "Add submodule child"  
>  
> git mv child child_old  
> git commit -m "Move child to child_old"  
>  
> git init child  
> git -C child commit --allow-empty -m "initial commit"  
> git submodule add https://example.com/child2.git child  
> git commit -m "Add a new submodule at child/"  
>  
> cat .gitmodules  
> 
>  

I could reproduce the behaviour, neat observation!!

> # What did you expect to happen  
>  
> I expect that both submodules have entries in the `.gitmodules` file.  
> Git submodule names are given by the path, so I expected some way  
> of resolving ambiguity, for example, Git appends ".path" in other  
> cases to disambiguate. So the expected `.gitmodules` content would  
> be:  
>  
> 
> [submodule "child"]  
>     path = child_old  
>     url = https://example.com/child.git  
> [submodule "child.path"]  
>     path = child  
>     url = https://example.com/child2.git  
> 
>  
> # What happened instead  
>  
> I see that there is only one entry in `.gitmodules` listing the  
> new submodule, under the name "child":  
>  
>  
> [submodule "child"]  
>     path = child  
>     url = https://example.com/child2.git  
> 
>  
> # How is this different from the expected result  
>  
> The old submodule (path = `child_old`) is not listed. The information  
> about the origin of the old subrepo is lost.  
>  
>  

I think the main issue is as follows 
in the module_add() function, 
add_data.sm_name defaults to the basename of the path you’re
adding, it re‑uses the already‑existing name “child” even though the
previous entry now points at child_old/.

Nothing in module_add() checks whether that name is already taken for a
different path, so the new write silently overwrites the old one.


The original code is something like this 

if (!add_data.sm_name)
	add_data.sm_name = add_data.sm_path;

if (check_submodule_name(add_data.sm_name))
	die(_("'%s' is not a valid submodule name"), add_data.sm_name);


Something like this in between works for me

const struct submodule *existing =
			submodule_from_name(the_repository,
					null_oid(the_hash_algo),
					add_data.sm_name);
	
			if (existing && strcmp(existing->path, add_data.sm_path)) {
				if (!force) {
					die(_("submodule name '%s' already used for path '%s' "
					      "(use --name to choose another or --force to overwrite)"),
					    add_data.sm_name, existing->path);
				}
				/* auto-disambiguate: append ".<basename>" */
				add_data.sm_name = xstrfmt("%s.%s",
							   add_data.sm_name,
							   basename(add_data.sm_path));
                        }


This is just a vague format. Just to compiler and check.

With this the behaviour now throws a fatal error and not change the 
existing submodules.

Something like this:
fatal: submodule name 'child' already used for path 'child_old' (use --name to choose another or --force to overwrite)
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        child/

nothing added to commit but untracked files present (use "git add" to track)

If this is good 
I can send a patch covering the patch with this specific fix and the test file
I don't think this needs a new test file, I will add in any existing one.

If anyone has a better idea I'm open to it too.

Thank You,

-Jayatheerth

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-05-06 17:30 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-05 13:41 Bug Report: git submodule overwrites submodules of same name but different path mlell08
2025-05-06 17:30 ` Re "Bug Report: git submodule overwrites submodules of same name but different path" K Jayatheerth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).