* [PATCH v7 0/2] Avoid submodule overwritten and skip redundant active entries
@ 2025-05-24 7:36 K Jayatheerth
2025-05-24 7:36 ` [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
2025-05-24 7:36 ` [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
0 siblings, 2 replies; 19+ messages in thread
From: K Jayatheerth @ 2025-05-24 7:36 UTC (permalink / raw)
To: git; +Cc: gitster, K Jayatheerth
This series of patch covers mainly two areas
1. The bug report where after submodule was moved and the path remained same
when a new submodule was added then it directly was overwriting the
moved submodule as the present submodule since the path matched.
2. The configure_added_submodule was writing submodule.<name>.active
entry, even when the new path is already matched by submodule.active
patterns.
Below is a helper function and 2 new tests with fixes of the above problem.
K Jayatheerth (2):
submodule: prevent overwriting .gitmodules entry on path reuse
submodule: skip redundant active entries when pattern covers path
builtin/submodule--helper.c | 60 +++++++++++++++++++++++++++-------
t/t7400-submodule-basic.sh | 23 +++++++++++++
t/t7413-submodule-is-active.sh | 15 +++++++++
3 files changed, 87 insertions(+), 11 deletions(-)
--
2.49.GIT
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-05-24 7:36 [PATCH v7 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
@ 2025-05-24 7:36 ` K Jayatheerth
2025-05-27 14:01 ` Junio C Hamano
2025-05-24 7:36 ` [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
1 sibling, 1 reply; 19+ messages in thread
From: K Jayatheerth @ 2025-05-24 7:36 UTC (permalink / raw)
To: git; +Cc: gitster, K Jayatheerth
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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path
2025-05-24 7:36 [PATCH v7 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
2025-05-24 7:36 ` [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
@ 2025-05-24 7:36 ` K Jayatheerth
2025-05-27 14:42 ` Junio C Hamano
1 sibling, 1 reply; 19+ messages in thread
From: K Jayatheerth @ 2025-05-24 7:36 UTC (permalink / raw)
To: git; +Cc: gitster, K Jayatheerth
configure_added_submodule always writes an explicit submodule.<name>.active
entry, even when the new path is already matched by submodule.active
patterns. This leads to unnecessary and cluttered configuration.
Introduce a single helper to centralize wildmatch-based pattern lookup.
In configure_added_submodule, wrap the active-entry write in a conditional
that only fires when that helper reports no existing pattern covers the
submodule’s path.
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
builtin/submodule--helper.c | 32 +++++++++++++++++++++-----------
t/t7413-submodule-is-active.sh | 15 +++++++++++++++
2 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9f6df833f0..8872c0fce3 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)
@@ -3323,6 +3325,24 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
return ret;
}
+static int submodule_active_matches_path(const char *path)
+{
+ const struct string_list *values;
+ size_t i;
+
+ if (git_config_get_string_multi("submodule.active", &values))
+ return 0;
+
+ for (i = 0; i < values->nr; i++) {
+ const char *pat = values->items[i].string;
+ if (!wildmatch(pat, path, 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+
static void configure_added_submodule(struct add_data *add_data)
{
char *key;
@@ -3370,17 +3390,7 @@ 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 (!git_config_get("submodule.active")) {
- /*
- * 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);
- git_config_set_gently(key, "true");
- free(key);
- }
- } else {
+ if (!submodule_active_matches_path(add_data->sm_path)) {
key = xstrfmt("submodule.%s.active", add_data->sm_name);
git_config_set_gently(key, "true");
free(key);
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index 9509dc18fd..a42060cac9 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 &&
+ ! git config --get submodule.lib/foo.active
+ )
+'
+
test_done
--
2.49.GIT
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-05-24 7:36 ` [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
@ 2025-05-27 14:01 ` Junio C Hamano
0 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2025-05-27 14:01 UTC (permalink / raw)
To: K Jayatheerth; +Cc: git
K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
> @@ -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)) {
Looks quite straight-forward to me. Great.
> + 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);
> + }
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path
2025-05-24 7:36 ` [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
@ 2025-05-27 14:42 ` Junio C Hamano
2025-05-29 4:23 ` JAYATHEERTH K
0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2025-05-27 14:42 UTC (permalink / raw)
To: K Jayatheerth; +Cc: git
K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
> configure_added_submodule always writes an explicit submodule.<name>.active
> entry, even when the new path is already matched by submodule.active
> patterns. This leads to unnecessary and cluttered configuration.
It might be less cluttered but is it a good thing?
Earlier, if submodule.active were b* (as documented in the "git help
submodules") and then you added submodule "baz", your "baz" would be
kept active even after you reconfigured submodule.active to another
pattern. With this change, that is no longer true, which to existing
users is a change in behaviour, and to some it may appear a regression.
According to that documentation, presense of submodule.<name>.url is
also a signal that the submodule is activated. If we are going to
omit setting .active because its path matches submodule.active,
shouldn't we also be checking if .url exists and omit setting
.active as well?
> Introduce a single helper to centralize wildmatch-based pattern lookup.
> In configure_added_submodule, wrap the active-entry write in a conditional
> that only fires when that helper reports no existing pattern covers the
> submodule’s path.
The new helper is a maintenance burden to keep in sync with
submodule.c:is_tree_submodule_active(); if we really want to go this
route, the patch should extract that "ah, submodule.active is set so
let's turn it into pathspec and see if the path matches" part of the
logic to make sure the logic is shared. But I am wondering if we
can do this without any new helper.
> @@ -3370,17 +3390,7 @@ 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 (!git_config_get("submodule.active")) {
> - /*
> - * 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);
> - git_config_set_gently(key, "true");
> - free(key);
> - }
> - } else {
> + if (!submodule_active_matches_path(add_data->sm_path)) {
I.e. Can we replace this if() condition with something like this?
/*
* Explicitly set 'submodule.<name>.active' only if it is not
* 'active' due to other reasons.
*/
if (!is_submodule_active(the_repository, add_data->sm_path)) {
That is, we ask if the submodule is already active (we are before
adding submodule.<name>.active for this thing---if may be active due
to submodule.active or submodule.<name>.url) and enter the block
only when it is not yet.
That way, this codepath does not have to worry about the exact logic
that determines if a submodule is 'active' even when its .active
configuration variable is not set.
> key = xstrfmt("submodule.%s.active", add_data->sm_name);
> git_config_set_gently(key, "true");
> free(key);
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path
2025-05-27 14:42 ` Junio C Hamano
@ 2025-05-29 4:23 ` JAYATHEERTH K
2025-06-04 4:22 ` Junio C Hamano
0 siblings, 1 reply; 19+ messages in thread
From: JAYATHEERTH K @ 2025-05-29 4:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Tue, May 27, 2025 at 8:12 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > configure_added_submodule always writes an explicit submodule.<name>.active
> > entry, even when the new path is already matched by submodule.active
> > patterns. This leads to unnecessary and cluttered configuration.
>
> It might be less cluttered but is it a good thing?
>
> Earlier, if submodule.active were b* (as documented in the "git help
> submodules") and then you added submodule "baz", your "baz" would be
> kept active even after you reconfigured submodule.active to another
> pattern. With this change, that is no longer true, which to existing
> users is a change in behaviour, and to some it may appear a regression.
>
> According to that documentation, presense of submodule.<name>.url is
> also a signal that the submodule is activated. If we are going to
> omit setting .active because its path matches submodule.active,
> shouldn't we also be checking if .url exists and omit setting
> .active as well?
>
> > Introduce a single helper to centralize wildmatch-based pattern lookup.
> > In configure_added_submodule, wrap the active-entry write in a conditional
> > that only fires when that helper reports no existing pattern covers the
> > submodule’s path.
>
> The new helper is a maintenance burden to keep in sync with
> submodule.c:is_tree_submodule_active(); if we really want to go this
> route, the patch should extract that "ah, submodule.active is set so
> let's turn it into pathspec and see if the path matches" part of the
> logic to make sure the logic is shared. But I am wondering if we
> can do this without any new helper.
>
I've actually done something like this, but I've wrapped the core logic within
the if else after checking [1]
> > @@ -3370,17 +3390,7 @@ 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 (!git_config_get("submodule.active")) {
> > - /*
> > - * 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);
> > - git_config_set_gently(key, "true");
> > - free(key);
> > - }
> > - } else {
> > + if (!submodule_active_matches_path(add_data->sm_path)) {
>
> I.e. Can we replace this if() condition with something like this?
>
> /*
> * Explicitly set 'submodule.<name>.active' only if it is not
> * 'active' due to other reasons.
> */
> if (!is_submodule_active(the_repository, add_data->sm_path)) {
>
> That is, we ask if the submodule is already active (we are before
> adding submodule.<name>.active for this thing---if may be active due
> to submodule.active or submodule.<name>.url) and enter the block
> only when it is not yet.
>
> That way, this codepath does not have to worry about the exact logic
> that determines if a submodule is 'active' even when its .active
> configuration variable is not set.
>
> > key = xstrfmt("submodule.%s.active", add_data->sm_name);
> > git_config_set_gently(key, "true");
> > free(key);
I've done the exact thing in a bit different way
Should I add this in the second patch, because this passes the tests too
including the new one created.
1- https://lore.kernel.org/git/20250518075436.75139-1-jayatheerthkulkarni2005@gmail.com/
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path
2025-05-29 4:23 ` JAYATHEERTH K
@ 2025-06-04 4:22 ` Junio C Hamano
2025-06-06 14:41 ` JAYATHEERTH K
0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2025-06-04 4:22 UTC (permalink / raw)
To: JAYATHEERTH K; +Cc: git
JAYATHEERTH K <jayatheerthkulkarni2005@gmail.com> writes:
>> The new helper is a maintenance burden to keep in sync with
>> submodule.c:is_tree_submodule_active(); if we really want to go this
>> route, the patch should extract that "ah, submodule.active is set so
>> let's turn it into pathspec and see if the path matches" part of the
>> logic to make sure the logic is shared. But I am wondering if we
>> can do this without any new helper.
>
> I've actually done something like this, but I've wrapped the core logic within
> the if else after checking [1]
I do not think I follow what you want to say here. Care to rephrase?
>> I.e. Can we replace this if() condition with something like this?
>>
>> /*
>> * Explicitly set 'submodule.<name>.active' only if it is not
>> * 'active' due to other reasons.
>> */
>> if (!is_submodule_active(the_repository, add_data->sm_path)) {
>>
>> That is, we ask if the submodule is already active (we are before
>> adding submodule.<name>.active for this thing---if may be active due
>> to submodule.active or submodule.<name>.url) and enter the block
>> only when it is not yet.
>>
>> That way, this codepath does not have to worry about the exact logic
>> that determines if a submodule is 'active' even when its .active
>> configuration variable is not set.
>>
>> > key = xstrfmt("submodule.%s.active", add_data->sm_name);
>> > git_config_set_gently(key, "true");
>> > free(key);
>
>
> I've done the exact thing in a bit different way
I am confused again, as this reads as if it is an oxymoron --- did
you write the exactly the same thing as suggested above, or did you
write something different?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path
2025-06-04 4:22 ` Junio C Hamano
@ 2025-06-06 14:41 ` JAYATHEERTH K
2025-06-08 3:27 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
0 siblings, 1 reply; 19+ messages in thread
From: JAYATHEERTH K @ 2025-06-06 14:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Wed, Jun 4, 2025 at 9:52 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> JAYATHEERTH K <jayatheerthkulkarni2005@gmail.com> writes:
>
> >> The new helper is a maintenance burden to keep in sync with
> >> submodule.c:is_tree_submodule_active(); if we really want to go this
> >> route, the patch should extract that "ah, submodule.active is set so
> >> let's turn it into pathspec and see if the path matches" part of the
> >> logic to make sure the logic is shared. But I am wondering if we
> >> can do this without any new helper.
> >
> > I've actually done something like this, but I've wrapped the core logic within
> > the if else after checking [1]
>
> I do not think I follow what you want to say here. Care to rephrase?
>
> >> I.e. Can we replace this if() condition with something like this?
> >>
> >> /*
> >> * Explicitly set 'submodule.<name>.active' only if it is not
> >> * 'active' due to other reasons.
> >> */
> >> if (!is_submodule_active(the_repository, add_data->sm_path)) {
> >>
> >> That is, we ask if the submodule is already active (we are before
> >> adding submodule.<name>.active for this thing---if may be active due
> >> to submodule.active or submodule.<name>.url) and enter the block
> >> only when it is not yet.
> >>
> >> That way, this codepath does not have to worry about the exact logic
> >> that determines if a submodule is 'active' even when its .active
> >> configuration variable is not set.
> >>
> >> > key = xstrfmt("submodule.%s.active", add_data->sm_name);
> >> > git_config_set_gently(key, "true");
> >> > free(key);
> >
> >
> > I've done the exact thing in a bit different way
>
> I am confused again, as this reads as if it is an oxymoron --- did
> you write the exactly the same thing as suggested above, or did you
> write something different?
>
Ok so I'm expand on why I thought this would work;
- if (!git_config_get("submodule.active")) {
- /*
- * 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)) {
+ if (git_config_get("submodule.active") || /* key absent */
+ git_config_get_string_multi("submodule.active", &values)) {
+ /* submodule.active is missing -> force-enable */
+ key = xstrfmt("submodule.%s.active", add_data->sm_name);
+ git_config_set_gently(key, "true");
+ free(key);
+ } else {
+ for (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);
git_config_set_gently(key, "true");
free(key);
}
- } else {
- key = xstrfmt("submodule.%s.active", add_data->sm_name);
- git_config_set_gently(key, "true");
- free(key);
}
}
To clarify: My aim with the current patch wasn't to create a
general-purpose helper, but to very precisely control the conditions
under which submodule.<name>.active = true is written during a git
submodule add operation. But I sure did have a thought that something
like this should exist.
The goals I considerat core when adding a new submodules should be
something like
a. The submodule should become active. This is the primary user expectation.
b.The configuration should be as clean and intentional as possible.
Avoid redundant entries if the global submodule.active already covers
the new submodule.
b. The configuration should explicitly reflect the active state if
there's no global policy, or if the global policy.
This was my underlying goal to be very specific.
Your suggestion to use if (!is_submodule_active(the_repository,
add_data->sm_path)) is elegant in its reuse of existing logic.
However, I find these following things
i. Scenario: submodule.active is globally ABSENT.
is_submodule_active() will return true (as submodules are active by
default in this case).
Therefore, !is_submodule_active() will be false.
Result with your suggestion: submodule.<name>.active = true would not
be written. The submodule is active by default, but this isn't
explicitly recorded for the submodule itself. Also a reason why the
test failed after digging a bit deeper this is the possible
explanation I found.
Specifically Test case 9 from t7413
test_expect_success 'is-active, submodule.active and submodule add' '
test_when_finished "rm -rf super2" &&
git init super2 &&
test_commit -C super2 initial &&
git -C super2 config --add submodule.active "sub*" &&
# submodule add should only add submodule.<name>.active
# to the config if not matched by the pathspec
git -C super2 submodule add ../sub sub1 &&
test_must_fail git -C super2 config --get submodule.sub1.active &&
git -C super2 submodule add ../sub mod &&
git -C super2 config --get submodule.mod.active
'
ii. My patch's behavior in this scenario (And not as a general purpose helper)
The first condition git_config_get("submodule.active") ||
git_config_get_string_multi("submodule.active", &values) evaluates to
true because submodule.active is absent.
Result with my patch: submodule.<name>.active = true is explicitly written.
Let me also clarify on what I mean by the _similar_ and _different_
statements I sent above.
The outcome is what I meant was _similar_ the patch link I sent in the
above mail, explicitly adds only when it doesn't find
new submodule to be active, and I verified it with a documented test
added as the tenth test in t7413 where it shouldn't add
an active twice and the outcome is what I found _similar_, the
_difference_ however are in the methods of getting to the outcome,
and adding guardrails. That's the underlying thought with that statement.
To reinforce the above ideas her e aer some points I would like to make:
The logic implemented in the else block (reading submodule.active and
iterating with wildmatch) is indeed a partial re-implementation of
what is_submodule_active might do internally. However:
It's highly localized to this specific decision point in git submodule add.
It's concerned only with the submodule.active pathspecs.
is_submodule_active might have broader considerations (as you hinted,
e.g., submodule.<name>.url's presence, though that's less relevant for
the .active flag itself).
The benefit of precise control over config generation for add might
outweigh the risk of this small, localized logic diverging, especially
since submodule.active's pathspec interpretation is fairly stable.
The core distinction is that is_submodule_active() answers "Is this
submodule considered active right now based on all existing rules and
defaults?". My patch attempts to answer a slightly different question
for git add: "Given the current global submodule.active policy, do we
need to explicitly set submodule.<name>.active = true for this new
submodule to ensure it's active and clearly recorded as such?"
And that is the reason why I had to mention the _different_ and _same_
statement within the same statement.
My approach aims to set submodule.<name>.active = true unless an
existing, valid, and matching global submodule.active pattern already
makes it active. This leads to what I feel is a more robust and
explicit configuration outcome from git submodule add.
Perhaps the ideal long-term solution would involve a more nuanced
helper function (which I'm more than happy to write, if I get the
green flag.), but for the immediate improvement to git add's behavior,
I believe this patch strikes a good balance, prioritizing explicit
activation and config clarity for newly added submodules.
Also to clarify :)
I tried to dig and clarify as much as I can
I maybe wrong in some places cause obviously I'm still relatively much
newer to the source code.
But I thought providing a huge para with what I thought would be
helpful for corrections.
Thank you for reading all the blab : )
- Jayatheerth
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries
2025-06-06 14:41 ` JAYATHEERTH K
@ 2025-06-08 3:27 ` K Jayatheerth
2025-06-08 3:27 ` [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
` (2 more replies)
0 siblings, 3 replies; 19+ messages in thread
From: K Jayatheerth @ 2025-06-08 3:27 UTC (permalink / raw)
To: jayatheerthkulkarni2005; +Cc: git, gitster
The first patch i.e. prevent overwriting .gitmodules entry on path reuse
is exactly the same
The second patch however i.e.
skip redundant active entries when pattern covers path
has changed logic
with the helper function being code maintainance because
of the duplicated logic. I've tried to wrap it as much as
possible to it's own core need without having to change it
unless the core way of submodules addition and active status
itself changes.
The CI was tested in which
the only compiler error I found was
osx-gcc
Which I suppose is already addressed,
apart from this all of the other tests
ran successfully which includes the
t7413 (prvious 9) and the new one.
K Jayatheerth (2):
submodule: prevent overwriting .gitmodules entry on path reuse
submodule: skip redundant active entries when pattern covers path
builtin/submodule--helper.c | 60 +++++++++++++++++++++++++++-------
t/t7400-submodule-basic.sh | 23 +++++++++++++
t/t7413-submodule-is-active.sh | 15 +++++++++
3 files changed, 87 insertions(+), 11 deletions(-)
--
2.49.GIT
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-06-08 3:27 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
@ 2025-06-08 3:27 ` K Jayatheerth
2025-07-09 2:50 ` D. Ben Knoble
2025-06-08 3:27 ` [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
2025-07-07 22:34 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries Junio C Hamano
2 siblings, 1 reply; 19+ messages in thread
From: K Jayatheerth @ 2025-06-08 3:27 UTC (permalink / raw)
To: jayatheerthkulkarni2005; +Cc: git, gitster
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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path
2025-06-08 3:27 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
2025-06-08 3:27 ` [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
@ 2025-06-08 3:27 ` K Jayatheerth
2025-07-09 2:50 ` D. Ben Knoble
2025-07-17 16:58 ` Junio C Hamano
2025-07-07 22:34 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries Junio C Hamano
2 siblings, 2 replies; 19+ messages in thread
From: K Jayatheerth @ 2025-06-08 3:27 UTC (permalink / raw)
To: jayatheerthkulkarni2005; +Cc: git, gitster
configure_added_submodule always writes an explicit submodule.<name>.active
entry, even when the new path is already matched by submodule.active
patterns. This leads to unnecessary and cluttered configuration.
change the logic to centralize wildmatch-based pattern lookup,
in configure_added_submodule. Wrap the active-entry write in a conditional
that only fires when that helper reports no existing pattern covers the
submodule’s path.
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
builtin/submodule--helper.c | 32 +++++++++++++++++++++-----------
t/t7413-submodule-is-active.sh | 15 +++++++++++++++
2 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9f6df833f0..514abe480e 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)
@@ -3328,6 +3330,9 @@ 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;
+ size_t i;
+ int matched = 0;
key = xstrfmt("submodule.%s.url", add_data->sm_name);
git_config_set_gently(key, add_data->realrepo);
@@ -3370,20 +3375,25 @@ 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 (!git_config_get("submodule.active")) {
- /*
- * 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);
- git_config_set_gently(key, "true");
- free(key);
- }
- } else {
+ if (git_config_get("submodule.active") || /* key absent */
+ git_config_get_string_multi("submodule.active", &values)) {
+ /* submodule.active is missing -> force-enable */
key = xstrfmt("submodule.%s.active", add_data->sm_name);
git_config_set_gently(key, "true");
free(key);
+ } else {
+ for (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);
+ git_config_set_gently(key, "true");
+ free(key);
+ }
}
}
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index 9509dc18fd..a42060cac9 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 &&
+ ! git config --get submodule.lib/foo.active
+ )
+'
+
test_done
--
2.49.GIT
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries
2025-06-08 3:27 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
2025-06-08 3:27 ` [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
2025-06-08 3:27 ` [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
@ 2025-07-07 22:34 ` Junio C Hamano
2 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2025-07-07 22:34 UTC (permalink / raw)
To: git; +Cc: K Jayatheerth
K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
> The first patch i.e. prevent overwriting .gitmodules entry on path reuse
> is exactly the same
>
> The second patch however i.e.
> skip redundant active entries when pattern covers path
> has changed logic
> with the helper function being code maintainance because
> of the duplicated logic. I've tried to wrap it as much as
> possible to it's own core need without having to change it
> unless the core way of submodules addition and active status
> itself changes.
>
> The CI was tested in which
> the only compiler error I found was
> osx-gcc
> Which I suppose is already addressed,
> apart from this all of the other tests
> ran successfully which includes the
> t7413 (prvious 9) and the new one.
>
>
> K Jayatheerth (2):
> submodule: prevent overwriting .gitmodules entry on path reuse
> submodule: skip redundant active entries when pattern covers path
Haven't seen any comment from others on this topic for quite a
while. How does this one look to those who do use submodules (I am
not one of them)?
Thanks.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path
2025-06-08 3:27 ` [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
@ 2025-07-09 2:50 ` D. Ben Knoble
2025-07-09 14:52 ` Junio C Hamano
2025-07-17 16:58 ` Junio C Hamano
1 sibling, 1 reply; 19+ messages in thread
From: D. Ben Knoble @ 2025-07-09 2:50 UTC (permalink / raw)
To: K Jayatheerth; +Cc: git, gitster
On Sat, Jun 7, 2025 at 11:28 PM K Jayatheerth
<jayatheerthkulkarni2005@gmail.com> wrote:
>
> configure_added_submodule always writes an explicit submodule.<name>.active
> entry, even when the new path is already matched by submodule.active
> patterns. This leads to unnecessary and cluttered configuration.
>
> change the logic to centralize wildmatch-based pattern lookup,
> in configure_added_submodule. Wrap the active-entry write in a conditional
> that only fires when that helper reports no existing pattern covers the
> submodule’s path.
I use submodules, but am not too familiar with their internals, so I
find it hard to follow the details here. Perhaps some examples showing
when the configuration becomes cluttered and what about it is
cluttered would help?
When I check a repo of mine that has ~50 submodules:
git config get submodule.active
.
git config get --show-names --regexp 'submodule.*active'
submodule.active .
So I'm not seeing this in practice, though I didn't try with the test
case in the patch.
[Nit: s/change/Change]
>
> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
> ---
> builtin/submodule--helper.c | 32 +++++++++++++++++++++-----------
> t/t7413-submodule-is-active.sh | 15 +++++++++++++++
> 2 files changed, 36 insertions(+), 11 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 9f6df833f0..514abe480e 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)
> @@ -3328,6 +3330,9 @@ 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;
> + size_t i;
> + int matched = 0;
>
> key = xstrfmt("submodule.%s.url", add_data->sm_name);
> git_config_set_gently(key, add_data->realrepo);
> @@ -3370,20 +3375,25 @@ 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 (!git_config_get("submodule.active")) {
> - /*
> - * If the submodule being added isn't already covered by the
> - * current configured pathspec, set the submodule's active flag
> - */
Do we want to lose this comment? The replacement below ("… ->
force-enable") is a bit terse for me and rather loses some
information.
> - if (!is_submodule_active(the_repository, add_data->sm_path)) {
> - key = xstrfmt("submodule.%s.active", add_data->sm_name);
> - git_config_set_gently(key, "true");
> - free(key);
> - }
> - } else {
> + if (git_config_get("submodule.active") || /* key absent */
> + git_config_get_string_multi("submodule.active", &values)) {
> + /* submodule.active is missing -> force-enable */
> key = xstrfmt("submodule.%s.active", add_data->sm_name);
> git_config_set_gently(key, "true");
> free(key);
> + } else {
> + for (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);
> + git_config_set_gently(key, "true");
> + free(key);
> + }
> }
> }
>
> diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
> index 9509dc18fd..a42060cac9 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 &&
> + ! git config --get submodule.lib/foo.active
(Not my area of expertise) Should this be test_must_fail?
> + )
> +'
> +
> test_done
> --
> 2.49.GIT
>
>
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-06-08 3:27 ` [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
@ 2025-07-09 2:50 ` D. Ben Knoble
2025-07-20 12:25 ` JAYATHEERTH K
0 siblings, 1 reply; 19+ messages in thread
From: D. Ben Knoble @ 2025-07-09 2:50 UTC (permalink / raw)
To: K Jayatheerth; +Cc: git, gitster
On Sat, Jun 7, 2025 at 11:28 PM K Jayatheerth
<jayatheerthkulkarni2005@gmail.com> wrote:
>
> 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.
I had to read the patch to figure out what "auto increment"
meant—perhaps some accompanying docs in `git help submodule`?
>
> 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;
> + }
> + }
This isn't typically what I'd expect --force to do, personally, though
in this case it allows me to proceed with an operation that wasn't
allowed otherwise.
Still, I wonder if a user might be confused by "I said 'child' and got
'child2'?"
> +
> + 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
This makes sense, though I was hoping (when I'd only skimmed the
message and not seen "refuse") that this would be permitted by some
clever trick. Oh well.
> + )
> +'
> +
> +
> test_done
> --
> 2.49.GIT
>
>
--
D. Ben Knoble
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path
2025-07-09 2:50 ` D. Ben Knoble
@ 2025-07-09 14:52 ` Junio C Hamano
0 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2025-07-09 14:52 UTC (permalink / raw)
To: D. Ben Knoble; +Cc: K Jayatheerth, git
"D. Ben Knoble" <ben.knoble@gmail.com> writes:
>> +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 &&
>> + ! git config --get submodule.lib/foo.active
>
> (Not my area of expertise) Should this be test_must_fail?
That is certainly better. "! git config ..." would succeed even
when that command segfaults and dumps core, which we may want to
notice.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path
2025-06-08 3:27 ` [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
2025-07-09 2:50 ` D. Ben Knoble
@ 2025-07-17 16:58 ` Junio C Hamano
2025-07-18 14:24 ` JAYATHEERTH K
1 sibling, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2025-07-17 16:58 UTC (permalink / raw)
To: K Jayatheerth; +Cc: git
K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
> + if (!matched) { /* no pattern matched -> force-enable */
> + key = xstrfmt("submodule.%s.active", add_data->sm_name);
> + git_config_set_gently(key, "true");
> + free(key);
> + }
Somehow these lines begin with SP and then HT. If you are going to
send an updated version, please make sure to fix the whitespace
issues around here.
In the meantime, I'll tweak the version I have in 'seen'.
Thanks.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path
2025-07-17 16:58 ` Junio C Hamano
@ 2025-07-18 14:24 ` JAYATHEERTH K
0 siblings, 0 replies; 19+ messages in thread
From: JAYATHEERTH K @ 2025-07-18 14:24 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Thu, Jul 17, 2025 at 10:28 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > + if (!matched) { /* no pattern matched -> force-enable */
> > + key = xstrfmt("submodule.%s.active", add_data->sm_name);
> > + git_config_set_gently(key, "true");
> > + free(key);
> > + }
>
> Somehow these lines begin with SP and then HT. If you are going to
> send an updated version, please make sure to fix the whitespace
> issues around here.
>
> In the meantime, I'll tweak the version I have in 'seen'.
>
> Thanks.
Damn, I missed the previous emails,
Actually my fault it is, I made some tweaks to my email
But anyways
I will go through and send an updated version
Thank you
- Jayatheerth
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-07-09 2:50 ` D. Ben Knoble
@ 2025-07-20 12:25 ` JAYATHEERTH K
2025-07-25 17:28 ` Ben Knoble
0 siblings, 1 reply; 19+ messages in thread
From: JAYATHEERTH K @ 2025-07-20 12:25 UTC (permalink / raw)
To: D. Ben Knoble; +Cc: git, gitster
On Wed, Jul 9, 2025 at 8:20 AM D. Ben Knoble <ben.knoble@gmail.com> wrote:
>
> On Sat, Jun 7, 2025 at 11:28 PM K Jayatheerth
> <jayatheerthkulkarni2005@gmail.com> wrote:
> >
> > 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.
>
> I had to read the patch to figure out what "auto increment"
> meant—perhaps some accompanying docs in `git help submodule`?
>
> >
> > 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;
> > + }
> > + }
>
> This isn't typically what I'd expect --force to do, personally, though
> in this case it allows me to proceed with an operation that wasn't
> allowed otherwise.
>
> Still, I wonder if a user might be confused by "I said 'child' and got
> 'child2'?"
>
Ok so while fixing the previous versions of my submissions
I got stumped at this, I found child<incremented val> to be intuitive
at that time
but I can see why it may not be intuitive too, I mean I could just
remove the previous child and add
the current data as the new child because that feels intuitive for force.
If that is something which is in the interest I could send the new
patches as soon as possible.
Thank You
- Jayatheerth
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse
2025-07-20 12:25 ` JAYATHEERTH K
@ 2025-07-25 17:28 ` Ben Knoble
0 siblings, 0 replies; 19+ messages in thread
From: Ben Knoble @ 2025-07-25 17:28 UTC (permalink / raw)
To: JAYATHEERTH K; +Cc: git, gitster
> Le 20 juil. 2025 à 08:25, JAYATHEERTH K <jayatheerthkulkarni2005@gmail.com> a écrit :
>
> On Wed, Jul 9, 2025 at 8:20 AM D. Ben Knoble <ben.knoble@gmail.com> wrote:
>>
>>> On Sat, Jun 7, 2025 at 11:28 PM K Jayatheerth
>>> <jayatheerthkulkarni2005@gmail.com> wrote:
>>>
>>> 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.
>>
>> I had to read the patch to figure out what "auto increment"
>> meant—perhaps some accompanying docs in `git help submodule`?
>>
>>>
>>> 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;
>>> + }
>>> + }
>>
>> This isn't typically what I'd expect --force to do, personally, though
>> in this case it allows me to proceed with an operation that wasn't
>> allowed otherwise.
>>
>> Still, I wonder if a user might be confused by "I said 'child' and got
>> 'child2'?"
>>
>
>
> Ok so while fixing the previous versions of my submissions
> I got stumped at this, I found child<incremented val> to be intuitive
> at that time
> but I can see why it may not be intuitive too, I mean I could just
> remove the previous child and add
> the current data as the new child because that feels intuitive for force.
> If that is something which is in the interest I could send the new
> patches as soon as possible.
That sounds more like a typical forceful operation to me.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2025-07-25 17:28 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-24 7:36 [PATCH v7 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
2025-05-24 7:36 ` [PATCH v7 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
2025-05-27 14:01 ` Junio C Hamano
2025-05-24 7:36 ` [PATCH v7 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
2025-05-27 14:42 ` Junio C Hamano
2025-05-29 4:23 ` JAYATHEERTH K
2025-06-04 4:22 ` Junio C Hamano
2025-06-06 14:41 ` JAYATHEERTH K
2025-06-08 3:27 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries K Jayatheerth
2025-06-08 3:27 ` [PATCH v8 1/2] submodule: prevent overwriting .gitmodules entry on path reuse K Jayatheerth
2025-07-09 2:50 ` D. Ben Knoble
2025-07-20 12:25 ` JAYATHEERTH K
2025-07-25 17:28 ` Ben Knoble
2025-06-08 3:27 ` [PATCH v8 2/2] submodule: skip redundant active entries when pattern covers path K Jayatheerth
2025-07-09 2:50 ` D. Ben Knoble
2025-07-09 14:52 ` Junio C Hamano
2025-07-17 16:58 ` Junio C Hamano
2025-07-18 14:24 ` JAYATHEERTH K
2025-07-07 22:34 ` [PATCH v8 0/2] Avoid submodule overwritten and skip redundant active entries Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).