From 8fc36c39d9f68f5c556f9d8a2713116824a83dd7 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:51 -0700 Subject: submodule--helper tests: add missing "display path" coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two locations in prepare_to_clone_next_submodule() that manually calculate the submodule display path. As discussed in the next commit the "Skipping" output isn't exactly what we want, but let's test how we behave now, before changing the existing behavior. Signed-off-by: Glen Choo Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7406-submodule-update.sh | 62 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 06d804e213..9a076e025f 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -1116,4 +1116,66 @@ test_expect_success 'submodule update --filter sets partial clone settings' ' test_cmp_config -C super-filter/submodule blob:none remote.origin.partialclonefilter ' +# NEEDSWORK: Clean up the tests so that we can reuse the test setup. +# Don't reuse the existing repos because the earlier tests have +# intentionally disruptive configurations. +test_expect_success 'setup clean recursive superproject' ' + git init bottom && + test_commit -C bottom "bottom" && + git init middle && + git -C middle submodule add ../bottom bottom && + git -C middle commit -m "middle" && + git init top && + git -C top submodule add ../middle middle && + git -C top commit -m "top" && + git clone --recurse-submodules top top-clean +' + +test_expect_success 'submodule update should skip unmerged submodules' ' + test_when_finished "rm -fr top-cloned" && + cp -r top-clean top-cloned && + + # Create an upstream commit in each repo, starting with bottom + test_commit -C bottom upstream_commit && + # Create middle commit + git -C middle/bottom fetch && + git -C middle/bottom checkout -f FETCH_HEAD && + git -C middle add bottom && + git -C middle commit -m "upstream_commit" && + # Create top commit + git -C top/middle fetch && + git -C top/middle checkout -f FETCH_HEAD && + git -C top add middle && + git -C top commit -m "upstream_commit" && + + # Create a downstream conflict + test_commit -C top-cloned/middle/bottom downstream_commit && + git -C top-cloned/middle add bottom && + git -C top-cloned/middle commit -m "downstream_commit" && + git -C top-cloned/middle fetch --recurse-submodules origin && + test_must_fail git -C top-cloned/middle merge origin/main && + + # Make the update of "middle" a no-op, otherwise we error out + # because of its unmerged state + test_config -C top-cloned submodule.middle.update !true && + git -C top-cloned submodule update --recursive 2>actual.err && + cat >expect.err <<-\EOF && + Skipping unmerged submodule middle//bottom + EOF + test_cmp expect.err actual.err +' + +test_expect_success 'submodule update --recursive skip submodules with strategy=none' ' + test_when_finished "rm -fr top-cloned" && + cp -r top-clean top-cloned && + + test_commit -C top-cloned/middle/bottom downstream_commit && + git -C top-cloned/middle config submodule.bottom.update none && + git -C top-cloned submodule update --recursive 2>actual.err && + cat >expect.err <<-\EOF && + Skipping submodule '\''../middle/'\'' + EOF + test_cmp expect.err actual.err +' + test_done -- cgit v1.3-5-g9baa From 618b8445d92c60d9e1e4609e7c56a941bb3862b9 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:52 -0700 Subject: submodule--helper update: use display path helper There are two locations in prepare_to_clone_next_submodule() that manually calculate the submodule display path, but should just use do_get_submodule_displaypath() for consistency. Do this replacement and reorder the code slightly to avoid computing the display path twice. Until the preceding commit this code had never been tested, with our newly added tests we can see that both these sites have been computing the display path incorrectly ever since they were introduced in 48308681b0 (git submodule update: have a dedicated helper for cloning, 2016-02-29) [1]: - The first hunk puts a "/" between recursive_prefix and ce->name, but recursive_prefix already ends with "/". - The second hunk calls relative_path() on recursive_prefix and ce->name, but relative_path() only makes sense when both paths share the same base directory. This is never the case here: - recursive_prefix is the path from the topmost superproject to the current submodule - ce->name is the path from the root of the current submodule to its submodule. so, e.g. recursive_prefix="super" and ce->name="submodule" produces displayname="../super" instead of "super/submodule". [1] I verified this by applying the tests to 48308681b0. Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 19 +++++-------------- t/t7406-submodule-update.sh | 4 ++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d60f6cd9de..08ca18af49 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1947,30 +1947,21 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, const char *update_string; enum submodule_update_type update_type; char *key; - struct strbuf displaypath_sb = STRBUF_INIT; + struct update_data *ud = suc->update_data; + char *displaypath = do_get_submodule_displaypath(ce->name, ud->prefix, + ud->recursive_prefix); struct strbuf sb = STRBUF_INIT; - const char *displaypath = NULL; int needs_cloning = 0; int need_free_url = 0; if (ce_stage(ce)) { - if (suc->update_data->recursive_prefix) - strbuf_addf(&sb, "%s/%s", suc->update_data->recursive_prefix, ce->name); - else - strbuf_addstr(&sb, ce->name); - strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf); + strbuf_addf(out, _("Skipping unmerged submodule %s"), displaypath); strbuf_addch(out, '\n'); goto cleanup; } sub = submodule_from_path(the_repository, null_oid(), ce->name); - if (suc->update_data->recursive_prefix) - displaypath = relative_path(suc->update_data->recursive_prefix, - ce->name, &displaypath_sb); - else - displaypath = ce->name; - if (!sub) { next_submodule_warn_missing(suc, out, displaypath); goto cleanup; @@ -2060,7 +2051,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, "--no-single-branch"); cleanup: - strbuf_release(&displaypath_sb); + free(displaypath); strbuf_release(&sb); if (need_free_url) free((void*)url); diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 9a076e025f..6cc07460dd 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -1160,7 +1160,7 @@ test_expect_success 'submodule update should skip unmerged submodules' ' test_config -C top-cloned submodule.middle.update !true && git -C top-cloned submodule update --recursive 2>actual.err && cat >expect.err <<-\EOF && - Skipping unmerged submodule middle//bottom + Skipping unmerged submodule middle/bottom EOF test_cmp expect.err actual.err ' @@ -1173,7 +1173,7 @@ test_expect_success 'submodule update --recursive skip submodules with strategy= git -C top-cloned/middle config submodule.bottom.update none && git -C top-cloned submodule update --recursive 2>actual.err && cat >expect.err <<-\EOF && - Skipping submodule '\''../middle/'\'' + Skipping submodule '\''middle/bottom'\'' EOF test_cmp expect.err actual.err ' -- cgit v1.3-5-g9baa From cb49e1e8d342795cbb31c204b5ab744ae9c2e1f3 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:53 -0700 Subject: submodule--helper: don't recreate recursive prefix update_submodule() uses duplicated code to compute update_data->displaypath and next.recursive_prefix. The latter is just the former with "/" appended to it, and since update_data->displaypath not changed outside of this statement, we can just reuse the already computed result. We can go one step further and remove the reference to next.recursive_prefix altogether. Since it is only used in update_data_to_args() (to compute the "--recursive-prefix" flag for the recursive update child process) we can just use the already computed .displaypath value of there. Delete the duplicated code, and remove the unnecessary reference to next.recursive_prefix. As a bonus, this fixes a memory leak where prefixed_path was never freed (this leak was first reported in [1]). [1] https://lore.kernel.org/git/877a45867ae368bf9e053caedcb6cf421e02344d.1655336146.git.gitgitgadget@gmail.com Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 08ca18af49..455e6766c3 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2417,9 +2417,10 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL); strvec_pushf(args, "--jobs=%d", update_data->max_jobs); - if (update_data->recursive_prefix) - strvec_pushl(args, "--recursive-prefix", - update_data->recursive_prefix, NULL); + if (update_data->displaypath) { + strvec_push(args, "--recursive-prefix"); + strvec_pushf(args, "%s/", update_data->displaypath); + } if (update_data->quiet) strvec_push(args, "--quiet"); if (update_data->force) @@ -2515,14 +2516,6 @@ static int update_submodule(struct update_data *update_data) struct update_data next = *update_data; int res; - if (update_data->recursive_prefix) - prefixed_path = xstrfmt("%s%s/", update_data->recursive_prefix, - update_data->sm_path); - else - prefixed_path = xstrfmt("%s/", update_data->sm_path); - - next.recursive_prefix = get_submodule_displaypath(prefixed_path, - update_data->prefix); next.prefix = NULL; oidcpy(&next.oid, null_oid()); oidcpy(&next.suboid, null_oid()); -- cgit v1.3-5-g9baa From 58cec298f1c2e55875c56afa1bcf3bbb41bc9586 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:54 -0700 Subject: submodule--helper: use correct display path helper Replace a chunk of code in update_submodule() with an equivalent do_get_submodule_displaypath() invocation. This is already tested by t/t7406-submodule-update.sh:'submodule update --init --recursive from subdirectory', so no tests are added. The two are equivalent because: - Exactly one of recursive_prefix|prefix is non-NULL at a time; prefix is set at the superproject level, and recursive_prefix is set when recursing into submodules. There is also a BUG() statement in get_submodule_displaypath() that asserts that both cannot be non-NULL. - In get_submodule_displaypath(), get_super_prefix() always returns NULL because "--super-prefix" is never passed. Thus calling it is equivalent to calling do_get_submodule_displaypath() with super_prefix = NULL. Therefore: - When recursive_prefix is non-NULL, prefix is NULL, and thus get_submodule_displaypath() just returns prefixed_path. This is identical to calling do_get_submodule_displaypath() with super_prefix = recursive_prefix because the return value is still the concatenation of recursive_prefix + update_data->sm_path. - When prefix is non-NULL, prefixed_path = update_data->sm_path. Thus calling get_submodule_displaypath() with prefixed_path is equivalent to calling do_get_submodule_displaypath() with update_data->sm_path Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 455e6766c3..811a08ead0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2464,19 +2464,11 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * static int update_submodule(struct update_data *update_data) { - char *prefixed_path; - ensure_core_worktree(update_data->sm_path); - if (update_data->recursive_prefix) - prefixed_path = xstrfmt("%s%s", update_data->recursive_prefix, - update_data->sm_path); - else - prefixed_path = xstrdup(update_data->sm_path); - - update_data->displaypath = get_submodule_displaypath(prefixed_path, - update_data->prefix); - free(prefixed_path); + update_data->displaypath = do_get_submodule_displaypath(update_data->sm_path, + update_data->prefix, + update_data->recursive_prefix); determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, update_data->update_default, -- cgit v1.3-5-g9baa From b0f8b21305fd53d0bd3fa32cc1ee9fa9026cf321 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Thu, 30 Jun 2022 19:11:55 -0700 Subject: submodule--helper: remove unused SUPPORT_SUPER_PREFIX flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the SUPPORT_SUPER_PREFIX flag from "add", "init" and "summary". For the "add" command it hasn't been used since [1], likewise for "init" and "summary" since [2] and [3], respectively. As implemented in 74866d75793 (git: make super-prefix option, 2016-10-07) the SUPPORT_SUPER_PREFIX flag in git.c applies for the entire command, but as implemented in 89c86265576 (submodule helper: support super prefix, 2016-12-08) we assert here in cmd_submodule__helper() that we're not getting the flag unexpectedly. 1. 8c8195e9c3e (submodule--helper: introduce add-clone subcommand, 2021-07-10) 2. 6e7c14e65c8 (submodule update --init: display correct path from submodule, 2017-01-06) 3. 1cf823d8f00 (submodule: remove unnecessary `prefix` based option logic, 2021-06-22) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 811a08ead0..f13202b90e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -3379,15 +3379,15 @@ static struct cmd_struct commands[] = { {"list", module_list, 0}, {"name", module_name, 0}, {"clone", module_clone, 0}, - {"add", module_add, SUPPORT_SUPER_PREFIX}, + {"add", module_add, 0}, {"update", module_update, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, - {"init", module_init, SUPPORT_SUPER_PREFIX}, + {"init", module_init, 0}, {"status", module_status, SUPPORT_SUPER_PREFIX}, {"sync", module_sync, SUPPORT_SUPER_PREFIX}, {"deinit", module_deinit, 0}, - {"summary", module_summary, SUPPORT_SUPER_PREFIX}, + {"summary", module_summary, 0}, {"push-check", push_check, 0}, {"absorbgitdirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, {"is-active", is_active, 0}, -- cgit v1.3-5-g9baa From d7a714fddc2062fd21fbeec779fb3b9034fc21c2 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:56 -0700 Subject: submodule--helper update: use --super-prefix Unlike the other subcommands, "git submodule--helper update" uses the "--recursive-prefix" flag instead of "--super-prefix". The two flags are otherwise identical (they only serve to compute the 'display path' of a submodule), except that there is a dedicated helper function to get the value of "--super-prefix". This inconsistency exists because "git submodule update" used to pass "--recursive-prefix" between shell and C (introduced in [1]) before "--super-prefix" was introduced (in [2]), and for simplicity, we kept this name when "git submodule--helper update" was created. Remove "--recursive-prefix" and its associated code from "git submodule--helper update", replacing it with "--super-prefix". To use "--super-prefix", module_update is marked with SUPPORT_SUPER_PREFIX. Note that module_clone must also be marked with SUPPORT_SUPER_PREFIX, otherwise the "git submodule--helper clone" subprocess will fail check because "--super-prefix" is propagated via the environment. [1] 48308681b0 (git submodule update: have a dedicated helper for cloning, 2016-02-29) [2] 74866d7579 (git: make super-prefix option, 2016-10-07) Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f13202b90e..ad1ef4e7bb 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -477,22 +477,18 @@ static int starts_with_dot_dot_slash(const char *const path) struct init_cb { const char *prefix; - const char *superprefix; unsigned int flags; }; #define INIT_CB_INIT { 0 } static void init_submodule(const char *path, const char *prefix, - const char *superprefix, unsigned int flags) + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* try superprefix from the environment, if it is not passed explicitly */ - if (!superprefix) - superprefix = get_super_prefix(); - displaypath = do_get_submodule_displaypath(path, prefix, superprefix); + displaypath = do_get_submodule_displaypath(path, prefix, get_super_prefix()); sub = submodule_from_path(the_repository, null_oid(), path); @@ -566,7 +562,7 @@ static void init_submodule(const char *path, const char *prefix, static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct init_cb *info = cb_data; - init_submodule(list_item->name, info->prefix, info->superprefix, info->flags); + init_submodule(list_item->name, info->prefix, info->flags); } static int module_init(int argc, const char **argv, const char *prefix) @@ -1878,7 +1874,6 @@ struct submodule_update_clone { struct update_data { const char *prefix; - const char *recursive_prefix; const char *displaypath; enum submodule_update_type update_default; struct object_id suboid; @@ -1949,7 +1944,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, char *key; struct update_data *ud = suc->update_data; char *displaypath = do_get_submodule_displaypath(ce->name, ud->prefix, - ud->recursive_prefix); + get_super_prefix()); struct strbuf sb = STRBUF_INIT; int needs_cloning = 0; int need_free_url = 0; @@ -2415,12 +2410,12 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * { enum submodule_update_type update_type = update_data->update_default; - strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL); - strvec_pushf(args, "--jobs=%d", update_data->max_jobs); if (update_data->displaypath) { - strvec_push(args, "--recursive-prefix"); + strvec_push(args, "--super-prefix"); strvec_pushf(args, "%s/", update_data->displaypath); } + strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL); + strvec_pushf(args, "--jobs=%d", update_data->max_jobs); if (update_data->quiet) strvec_push(args, "--quiet"); if (update_data->force) @@ -2468,7 +2463,7 @@ static int update_submodule(struct update_data *update_data) update_data->displaypath = do_get_submodule_displaypath(update_data->sm_path, update_data->prefix, - update_data->recursive_prefix); + get_super_prefix()); determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, update_data->update_default, @@ -2592,10 +2587,6 @@ static int module_update(int argc, const char **argv, const char *prefix) OPT_STRING(0, "prefix", &opt.prefix, N_("path"), N_("path into the working tree")), - OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix, - N_("path"), - N_("path into the working tree, across nested " - "submodule boundaries")), OPT_SET_INT(0, "checkout", &opt.update_default, N_("use the 'checkout' update strategy (default)"), SM_UPDATE_CHECKOUT), @@ -2681,7 +2672,6 @@ static int module_update(int argc, const char **argv, const char *prefix) module_list_active(&list); info.prefix = opt.prefix; - info.superprefix = opt.recursive_prefix; if (opt.quiet) info.flags |= OPT_QUIET; @@ -3378,9 +3368,9 @@ struct cmd_struct { static struct cmd_struct commands[] = { {"list", module_list, 0}, {"name", module_name, 0}, - {"clone", module_clone, 0}, + {"clone", module_clone, SUPPORT_SUPER_PREFIX}, {"add", module_add, 0}, - {"update", module_update, 0}, + {"update", module_update, SUPPORT_SUPER_PREFIX}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, {"init", module_init, 0}, -- cgit v1.3-5-g9baa From 5ad87271cfab6edb8eb6ad736747cd1a4f42bf83 Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Thu, 30 Jun 2022 19:11:57 -0700 Subject: submodule--helper: remove display path helper All invocations of do_get_submodule_displaypath() pass get_super_prefix() as the super_prefix arg, which is exactly the same as get_submodule_displaypath(). Replace all calls to do_get_submodule_displaypath() with get_submodule_displaypath(), and since it has no more callers, remove it. Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ad1ef4e7bb..fac52ade5e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -118,10 +118,11 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } -static char *do_get_submodule_displaypath(const char *path, - const char *prefix, - const char *super_prefix) +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) { + const char *super_prefix = get_super_prefix(); + if (prefix && super_prefix) { BUG("cannot have prefix '%s' and superprefix '%s'", prefix, super_prefix); @@ -137,13 +138,6 @@ static char *do_get_submodule_displaypath(const char *path, } } -/* the result should be freed by the caller. */ -static char *get_submodule_displaypath(const char *path, const char *prefix) -{ - const char *super_prefix = get_super_prefix(); - return do_get_submodule_displaypath(path, prefix, super_prefix); -} - static char *compute_rev_name(const char *sub_path, const char* object_id) { struct strbuf sb = STRBUF_INIT; @@ -488,7 +482,7 @@ static void init_submodule(const char *path, const char *prefix, struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - displaypath = do_get_submodule_displaypath(path, prefix, get_super_prefix()); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(the_repository, null_oid(), path); @@ -1943,8 +1937,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, enum submodule_update_type update_type; char *key; struct update_data *ud = suc->update_data; - char *displaypath = do_get_submodule_displaypath(ce->name, ud->prefix, - get_super_prefix()); + char *displaypath = get_submodule_displaypath(ce->name, ud->prefix); struct strbuf sb = STRBUF_INIT; int needs_cloning = 0; int need_free_url = 0; @@ -2461,9 +2454,8 @@ static int update_submodule(struct update_data *update_data) { ensure_core_worktree(update_data->sm_path); - update_data->displaypath = do_get_submodule_displaypath(update_data->sm_path, - update_data->prefix, - get_super_prefix()); + update_data->displaypath = get_submodule_displaypath( + update_data->sm_path, update_data->prefix); determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, update_data->update_default, -- cgit v1.3-5-g9baa