git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse

From
K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
Date
May 24, 2025, 07:36 UTC
Message-ID
<20250524073628.58944-2-jayatheerthkulkarni2005@gmail.com>
In-Reply-To
<20250524073628.58944-1-jayatheerthkulkarni2005@gmail.com>

Adding a submodule at a path that previously hosted another submodule (e.g., 'child') reuses the submodule name derived from the path. If the original submodule was only moved (e.g., to 'child_old') and not renamed, this silently overwrites its configuration in .gitmodules.

This behavior loses user configuration and causes confusion when the original submodule is expected to remain intact. It assumes that the path-derived name is always safe to reuse, even though the name might still be in use elsewhere in the repository.

Teach `module_add()` to check if the computed submodule name already exists in the repository's submodule config, and if so, refuse the operation unless the user explicitly renames or uses force to auto increment.

Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 builtin/submodule--helper.c | 28 ++++++++++++++++++++++++++++
 t/t7400-submodule-basic.sh  | 23 +++++++++++++++++++++++
 2 files changed, 51 insertions(+)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 53da2116dd..9f6df833f0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3444,6 +3444,10 @@ static int module_add(int argc, const char **argv, const char *prefix,
 	struct add_data add_data = ADD_DATA_INIT;
 	const char *ref_storage_format = NULL;
 	char *to_free = NULL;
+	const struct submodule *existing;
+	struct strbuf buf = STRBUF_INIT;
+	int i;
+	char *sm_name_to_free = NULL;
 	struct option options[] = {
 		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
 			   N_("branch of repository to add as submodule")),
@@ -3546,6 +3550,29 @@ static int module_add(int argc, const char **argv, const char *prefix,
 	if(!add_data.sm_name)
 		add_data.sm_name = add_data.sm_path;
 
+	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'"),
+			add_data.sm_name, existing->path);
+		}
+
+		/* --force: build <name><n> until unique */
+		for (i = 1; ; i++) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "%s%d", add_data.sm_name, i);
+			if (!submodule_from_name(the_repository,
+						null_oid(the_hash_algo),
+						buf.buf)) {
+				break;
+			}
+		}
+
+		add_data.sm_name = sm_name_to_free = strbuf_detach(&buf, NULL);
+	}
 	if (check_submodule_name(add_data.sm_name))
 		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
 
@@ -3561,6 +3588,7 @@ static int module_add(int argc, const char **argv, const char *prefix,
 
 	ret = 0;
 cleanup:
+	free(sm_name_to_free);
 	free(add_data.sm_path);
 	free(to_free);
 	strbuf_release(&sb);
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index d6a501d453..f5514decab 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -1482,4 +1482,27 @@ test_expect_success '`submodule init` and `init.templateDir`' '
 	)
 '
 
+test_expect_success 'submodule add fails when name is reused' '
+	git init test-submodule &&
+	(
+		cd test-submodule &&
+		git commit --allow-empty -m init &&
+
+		git init ../child-origin &&
+		git -C ../child-origin commit --allow-empty -m init &&
+
+		git submodule add ../child-origin child &&
+		git commit -m "Add submodule child" &&
+
+		git mv child child_old &&
+		git commit -m "Move child to child_old" &&
+
+		# Now adding a *new* repo at the old name must fail
+		git init ../child2-origin &&
+		git -C ../child2-origin commit --allow-empty -m init &&
+		test_must_fail git submodule add ../child2-origin child
+	)
+'
+
+
 test_done
-- 
2.49.GIT
Previous: K JayatheerthNext: Junio C Hamano
Message 2 of 19 in “Avoid submodule overwritten and skip redundant active entries”
  1. 0/2 Avoid submodule overwritten and skip redundant active entriesK Jayatheerth, May 24, 2025
  2. 1/2 submodule: prevent overwriting .gitmodules entry on path reuseK Jayatheerth, May 24, 2025
  3. Junio C HamanoMay 27, 2025
  4. 2/2 submodule: skip redundant active entries when pattern covers pathK Jayatheerth, May 24, 2025
  5. Junio C HamanoMay 27, 2025
  6. JAYATHEERTH KMay 29, 2025
  7. Junio C HamanoJun 4, 2025
  8. JAYATHEERTH KJun 6, 2025
  9. 0/2 Avoid submodule overwritten and skip redundant active entriesK Jayatheerth, Jun 8, 2025
  10. 1/2 submodule: prevent overwriting .gitmodules entry on path reuseK Jayatheerth, Jun 8, 2025
  11. D. Ben KnobleJul 9, 2025
  12. JAYATHEERTH KJul 20, 2025
  13. Ben KnobleJul 25, 2025
  14. 2/2 submodule: skip redundant active entries when pattern covers pathK Jayatheerth, Jun 8, 2025
  15. D. Ben KnobleJul 9, 2025
  16. Junio C HamanoJul 9, 2025
  17. Junio C HamanoJul 17, 2025
  18. JAYATHEERTH KJul 18, 2025
  19. Junio C HamanoJul 7, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.