Skip to content

Commit 1fa06ce

Browse files
jayatheerthkulkarnigitster
authored andcommitted
submodule: prevent overwriting .gitmodules on path reuse
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 the submodule or uses the --force option, which will automatically generate a unique name by appending a number (e.g., child1). Signed-off-by: K Jayatheerth <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cb96e16 commit 1fa06ce

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

Documentation/git-submodule.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,13 @@ OPTIONS
307307
--force::
308308
This option is only valid for add, deinit and update commands.
309309
When running add, allow adding an otherwise ignored submodule path.
310+
This option is also used to bypass a check that the submodule's name
311+
is not already in use. By default, 'git submodule add' will fail if
312+
the proposed name (which is derived from the path) is already registered
313+
for another submodule in the repository. Using '--force' allows the command
314+
to proceed by automatically generating a unique name by appending a number
315+
to the conflicting name (e.g., if a submodule named 'child' exists, it will
316+
try 'child1', and so on).
310317
When running deinit the submodule working trees will be removed even
311318
if they contain local changes.
312319
When running update (only effective with the checkout procedure),

builtin/submodule--helper.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,6 +3444,10 @@ static int module_add(int argc, const char **argv, const char *prefix,
34443444
struct add_data add_data = ADD_DATA_INIT;
34453445
const char *ref_storage_format = NULL;
34463446
char *to_free = NULL;
3447+
const struct submodule *existing;
3448+
struct strbuf buf = STRBUF_INIT;
3449+
int i;
3450+
char *sm_name_to_free = NULL;
34473451
struct option options[] = {
34483452
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
34493453
N_("branch of repository to add as submodule")),
@@ -3546,6 +3550,28 @@ static int module_add(int argc, const char **argv, const char *prefix,
35463550
if(!add_data.sm_name)
35473551
add_data.sm_name = add_data.sm_path;
35483552

3553+
existing = submodule_from_name(the_repository,
3554+
null_oid(the_hash_algo),
3555+
add_data.sm_name);
3556+
3557+
if (existing && strcmp(existing->path, add_data.sm_path)) {
3558+
if (!force) {
3559+
die(_("submodule name '%s' already used for path '%s'"),
3560+
add_data.sm_name, existing->path);
3561+
}
3562+
/* --force: build <name><n> until unique */
3563+
for (i = 1; ; i++) {
3564+
strbuf_reset(&buf);
3565+
strbuf_addf(&buf, "%s%d", add_data.sm_name, i);
3566+
if (!submodule_from_name(the_repository,
3567+
null_oid(the_hash_algo),
3568+
buf.buf)) {
3569+
break;
3570+
}
3571+
}
3572+
add_data.sm_name = sm_name_to_free = strbuf_detach(&buf, NULL);
3573+
}
3574+
35493575
if (check_submodule_name(add_data.sm_name))
35503576
die(_("'%s' is not a valid submodule name"), add_data.sm_name);
35513577

@@ -3561,6 +3587,7 @@ static int module_add(int argc, const char **argv, const char *prefix,
35613587

35623588
ret = 0;
35633589
cleanup:
3590+
free(sm_name_to_free);
35643591
free(add_data.sm_path);
35653592
free(to_free);
35663593
strbuf_release(&sb);

t/t7400-submodule-basic.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,4 +1482,26 @@ test_expect_success '`submodule init` and `init.templateDir`' '
14821482
)
14831483
'
14841484

1485+
test_expect_success 'submodule add fails when name is reused' '
1486+
git init test-submodule &&
1487+
(
1488+
cd test-submodule &&
1489+
git commit --allow-empty -m init &&
1490+
1491+
git init ../child-origin &&
1492+
git -C ../child-origin commit --allow-empty -m init &&
1493+
1494+
git submodule add ../child-origin child &&
1495+
git commit -m "Add submodule child" &&
1496+
1497+
git mv child child_old &&
1498+
git commit -m "Move child to child_old" &&
1499+
1500+
# Now adding a *new* repo at the old name must fail
1501+
git init ../child2-origin &&
1502+
git -C ../child2-origin commit --allow-empty -m init &&
1503+
test_must_fail git submodule add ../child2-origin child
1504+
)
1505+
'
1506+
14851507
test_done

0 commit comments

Comments
 (0)