diff options
| -rw-r--r-- | Documentation/git-submodule.adoc | 7 | ||||
| -rw-r--r-- | builtin/submodule--helper.c | 50 | ||||
| -rwxr-xr-x | t/t7400-submodule-basic.sh | 23 | ||||
| -rwxr-xr-x | t/t7413-submodule-is-active.sh | 15 |
4 files changed, 89 insertions, 6 deletions
diff --git a/Documentation/git-submodule.adoc b/Documentation/git-submodule.adoc index 87d8e0f0c5..503c84a200 100644 --- a/Documentation/git-submodule.adoc +++ b/Documentation/git-submodule.adoc @@ -307,6 +307,13 @@ OPTIONS --force:: This option is only valid for add, deinit and update commands. When running add, allow adding an otherwise ignored submodule path. + This option is also used to bypass a check that the submodule's name + is not already in use. By default, 'git submodule add' will fail if + the proposed name (which is derived from the path) is already registered + for another submodule in the repository. Using '--force' allows the command + to proceed by automatically generating a unique name by appending a number + to the conflicting name (e.g., if a submodule named 'child' exists, it will + try 'child1', and so on). When running deinit the submodule working trees will be removed even if they contain local changes. When running update (only effective with the checkout procedure), diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 28f34f7bc1..07a1935cbe 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -32,6 +32,8 @@ #include "advice.h" #include "branch.h" #include "list-objects-filter-options.h" +#include "wildmatch.h" +#include "strbuf.h" #define OPT_QUIET (1 << 0) #define OPT_CACHED (1 << 1) @@ -3307,6 +3309,8 @@ static void configure_added_submodule(struct add_data *add_data) char *key; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; + const struct string_list *values; + int matched = 0; key = xstrfmt("submodule.%s.url", add_data->sm_name); repo_config_set_gently(the_repository, key, add_data->realrepo); @@ -3349,20 +3353,28 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!repo_config_get(the_repository, "submodule.active")) { + if (repo_config_get(the_repository, "submodule.active") || /* key absent */ + repo_config_get_string_multi(the_repository, "submodule.active", &values)) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag */ - if (!is_submodule_active(the_repository, add_data->sm_path)) { + key = xstrfmt("submodule.%s.active", add_data->sm_name); + repo_config_set_gently(the_repository, key, "true"); + free(key); + } else { + for (size_t i = 0; i < values->nr; i++) { + const char *pat = values->items[i].string; + if (!wildmatch(pat, add_data->sm_path, 0)) { /* match found */ + matched = 1; + break; + } + } + if (!matched) { /* no pattern matched -> force-enable */ key = xstrfmt("submodule.%s.active", add_data->sm_name); repo_config_set_gently(the_repository, key, "true"); free(key); } - } else { - key = xstrfmt("submodule.%s.active", add_data->sm_name); - repo_config_set_gently(the_repository, key, "true"); - free(key); } } @@ -3423,6 +3435,9 @@ 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; + 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")), @@ -3525,6 +3540,28 @@ 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 (int 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); @@ -3540,6 +3577,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..fd3e7e355e 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 2>err && + test_grep "already used for" err + ) +' + test_done diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh index 9509dc18fd..6fd3b870de 100755 --- a/t/t7413-submodule-is-active.sh +++ b/t/t7413-submodule-is-active.sh @@ -124,4 +124,19 @@ test_expect_success 'is-active, submodule.active and submodule add' ' git -C super2 config --get submodule.mod.active ' +test_expect_success 'submodule add skips redundant active entry' ' + git init repo && + ( + cd repo && + git config submodule.active "lib/*" && + git commit --allow-empty -m init && + + git init ../lib-origin && + git -C ../lib-origin commit --allow-empty -m init && + + git submodule add ../lib-origin lib/foo && + test_must_fail git config --get submodule.lib/foo.active + ) +' + test_done |
