Skip to content

Commit 8982c5e

Browse files
committed
Merge branch 'kj/renamed-submodule'
The case where a new submodule takes a path where used to be a completely different subproject is now dealt a bit better than before. * kj/renamed-submodule: fixup! submodule: skip redundant active entries when pattern covers path fixup! submodule: prevent overwriting .gitmodules on path reuse submodule: skip redundant active entries when pattern covers path submodule: prevent overwriting .gitmodules on path reuse
2 parents 2823d92 + 5ed8c5b commit 8982c5e

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
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: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include "advice.h"
3333
#include "branch.h"
3434
#include "list-objects-filter-options.h"
35+
#include "wildmatch.h"
36+
#include "strbuf.h"
3537

3638
#define OPT_QUIET (1 << 0)
3739
#define OPT_CACHED (1 << 1)
@@ -3307,6 +3309,8 @@ static void configure_added_submodule(struct add_data *add_data)
33073309
char *key;
33083310
struct child_process add_submod = CHILD_PROCESS_INIT;
33093311
struct child_process add_gitmodules = CHILD_PROCESS_INIT;
3312+
const struct string_list *values;
3313+
int matched = 0;
33103314

33113315
key = xstrfmt("submodule.%s.url", add_data->sm_name);
33123316
repo_config_set_gently(the_repository, key, add_data->realrepo);
@@ -3349,20 +3353,28 @@ static void configure_added_submodule(struct add_data *add_data)
33493353
* is_submodule_active(), since that function needs to find
33503354
* out the value of "submodule.active" again anyway.
33513355
*/
3352-
if (!repo_config_get(the_repository, "submodule.active")) {
3356+
if (repo_config_get(the_repository, "submodule.active") || /* key absent */
3357+
repo_config_get_string_multi(the_repository, "submodule.active", &values)) {
33533358
/*
33543359
* If the submodule being added isn't already covered by the
33553360
* current configured pathspec, set the submodule's active flag
33563361
*/
3357-
if (!is_submodule_active(the_repository, add_data->sm_path)) {
3362+
key = xstrfmt("submodule.%s.active", add_data->sm_name);
3363+
repo_config_set_gently(the_repository, key, "true");
3364+
free(key);
3365+
} else {
3366+
for (size_t i = 0; i < values->nr; i++) {
3367+
const char *pat = values->items[i].string;
3368+
if (!wildmatch(pat, add_data->sm_path, 0)) { /* match found */
3369+
matched = 1;
3370+
break;
3371+
}
3372+
}
3373+
if (!matched) { /* no pattern matched -> force-enable */
33583374
key = xstrfmt("submodule.%s.active", add_data->sm_name);
33593375
repo_config_set_gently(the_repository, key, "true");
33603376
free(key);
33613377
}
3362-
} else {
3363-
key = xstrfmt("submodule.%s.active", add_data->sm_name);
3364-
repo_config_set_gently(the_repository, key, "true");
3365-
free(key);
33663378
}
33673379
}
33683380

@@ -3423,6 +3435,9 @@ static int module_add(int argc, const char **argv, const char *prefix,
34233435
struct add_data add_data = ADD_DATA_INIT;
34243436
const char *ref_storage_format = NULL;
34253437
char *to_free = NULL;
3438+
const struct submodule *existing;
3439+
struct strbuf buf = STRBUF_INIT;
3440+
char *sm_name_to_free = NULL;
34263441
struct option options[] = {
34273442
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
34283443
N_("branch of repository to add as submodule")),
@@ -3525,6 +3540,28 @@ static int module_add(int argc, const char **argv, const char *prefix,
35253540
if(!add_data.sm_name)
35263541
add_data.sm_name = add_data.sm_path;
35273542

3543+
existing = submodule_from_name(the_repository,
3544+
null_oid(the_hash_algo),
3545+
add_data.sm_name);
3546+
3547+
if (existing && strcmp(existing->path, add_data.sm_path)) {
3548+
if (!force) {
3549+
die(_("submodule name '%s' already used for path '%s'"),
3550+
add_data.sm_name, existing->path);
3551+
}
3552+
/* --force: build <name><n> until unique */
3553+
for (int i = 1; ; i++) {
3554+
strbuf_reset(&buf);
3555+
strbuf_addf(&buf, "%s%d", add_data.sm_name, i);
3556+
if (!submodule_from_name(the_repository,
3557+
null_oid(the_hash_algo),
3558+
buf.buf)) {
3559+
break;
3560+
}
3561+
}
3562+
add_data.sm_name = sm_name_to_free = strbuf_detach(&buf, NULL);
3563+
}
3564+
35283565
if (check_submodule_name(add_data.sm_name))
35293566
die(_("'%s' is not a valid submodule name"), add_data.sm_name);
35303567

@@ -3540,6 +3577,7 @@ static int module_add(int argc, const char **argv, const char *prefix,
35403577

35413578
ret = 0;
35423579
cleanup:
3580+
free(sm_name_to_free);
35433581
free(add_data.sm_path);
35443582
free(to_free);
35453583
strbuf_release(&sb);

t/t7400-submodule-basic.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,4 +1482,27 @@ 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 2>err &&
1504+
test_grep "already used for" err
1505+
)
1506+
'
1507+
14851508
test_done

t/t7413-submodule-is-active.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,19 @@ test_expect_success 'is-active, submodule.active and submodule add' '
124124
git -C super2 config --get submodule.mod.active
125125
'
126126

127+
test_expect_success 'submodule add skips redundant active entry' '
128+
git init repo &&
129+
(
130+
cd repo &&
131+
git config submodule.active "lib/*" &&
132+
git commit --allow-empty -m init &&
133+
134+
git init ../lib-origin &&
135+
git -C ../lib-origin commit --allow-empty -m init &&
136+
137+
git submodule add ../lib-origin lib/foo &&
138+
test_must_fail git config --get submodule.lib/foo.active
139+
)
140+
'
141+
127142
test_done

0 commit comments

Comments
 (0)