# [PATCH] submodule--helper: set alternateLocation for cloned submodules

7 messages from 2016-12-07 to 2016-12-07. Participants: vi0oss@gmail.com, Stefan Beller, Junio C Hamano, vi0oss.
Thread: https://gitlist.dev/t/44644

## vi0oss@gmail.com, 2016-12-07 18:42

Subject: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <20161207184248.6130-1-vi0oss@gmail.com>
URL: https://gitlist.dev/e/20161207184248.6130-1-vi0oss%40gmail.com

```
From: Vitaly _Vi Shukela <vi0oss@gmail.com>

Git v2.11 introduced "git clone --recursive --referece ...",
but it didn't put the alternates for _nested_ submodules.

This patch makes all not just the root repository, but also
all submodules (recursively) have submodule.alternateLocation
and submodule.alternateErrorStrategy configured, making Git
search for possible alternates for nested submodules as well.

As submodule's alternate target does not end in .git/objects
(rather .git/modules/qqqqqq/objects), this alternate target
path restriction for in add_possible_reference_from_superproject
relates from "*.git/objects" to just */objects".

New tests have been added to t7408-submodule-reference.

Signed-off-by: Vitaly _Vi Shukela <vi0oss@gmail.com>
---
 builtin/submodule--helper.c    | 24 ++++++++++++--
 t/t7408-submodule-reference.sh | 73 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4beeda5..93dae62 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -498,9 +498,9 @@ static int add_possible_reference_from_superproject(
 
 	/*
 	 * If the alternate object store is another repository, try the
-	 * standard layout with .git/modules/<name>/objects
+	 * standard layout with .git/(modules/<name>)+/objects
 	 */
-	if (ends_with(alt->path, ".git/objects")) {
+	if (ends_with(alt->path, "/objects")) {
 		char *sm_alternate;
 		struct strbuf sb = STRBUF_INIT;
 		struct strbuf err = STRBUF_INIT;
@@ -672,6 +672,26 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 		die(_("could not get submodule directory for '%s'"), path);
 	git_config_set_in_file(p, "core.worktree",
 			       relative_path(path, sm_gitdir, &rel_path));
+
+	/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
+	{
+		char *sm_alternate = NULL, *error_strategy = NULL;
+
+		git_config_get_string("submodule.alternateLocation", &sm_alternate);
+		if (sm_alternate) {
+			git_config_set_in_file(p, "submodule.alternateLocation",
+						   sm_alternate);
+		}
+		git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+		if (error_strategy) {
+			git_config_set_in_file(p, "submodule.alternateErrorStrategy",
+						   error_strategy);
+		}
+
+		free(sm_alternate);
+		free(error_strategy);
+	}
+
 	strbuf_release(&sb);
 	strbuf_release(&rel_path);
 	free(sm_gitdir);
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index 1c1e289..7b64725 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -125,4 +125,77 @@ test_expect_success 'ignoring missing submodule alternates passes clone and subm
 	)
 '
 
+test_expect_success 'preparing second superproject with a nested submodule' '
+	test_create_repo supersuper &&
+	(
+		cd supersuper &&
+		echo I am super super. >file &&
+		git add file &&
+		git commit -m B-super-super-initial
+		git submodule add "file://$base_dir/super" subwithsub &&
+		git commit -m B-super-super-added &&
+		git submodule update --init --recursive &&
+		git repack -ad
+	) &&
+	echo not cleaning supersuper
+'
+
+# At this point there are three root-level positories: A, B, super and super2
+
+test_expect_success 'nested submodule alternate in works and is actually used' '
+	test_when_finished "rm -rf supersuper-clone" &&
+	git clone --recursive --reference supersuper supersuper supersuper-clone &&
+	(
+		cd supersuper-clone &&
+		# test superproject has alternates setup correctly
+		test_alternate_is_used .git/objects/info/alternates . &&
+		# immediate submodule has alternate:
+		test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub
+		# nested submodule also has alternate:
+		test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
+	)
+'
+
+test_expect_success 'missing nested submodule alternate fails clone and submodule update' '
+	test_when_finished "rm -rf supersuper-clone supersuper2" &&
+	git clone supersuper supersuper2 &&
+	(
+		cd supersuper2 &&
+		git submodule update --init
+	) &&
+	test_must_fail git clone --recursive --reference supersuper2 supersuper2 supersuper-clone &&
+	(
+		cd supersuper-clone &&
+		# test superproject has alternates setup correctly
+		test_alternate_is_used .git/objects/info/alternates . &&
+		# update of the submodule fails
+		test_must_fail git submodule update --init --recursive &&
+		# immediate submodule has alternate:
+		test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub
+		# but nested submodule has no alternate:
+		test_must_fail test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
+	)
+'
+
+test_expect_success 'missing nested submodule alternate in --reference-if-able mode' '
+	test_when_finished "rm -rf supersuper-clone supersuper2" &&
+	git clone supersuper supersuper2 &&
+	(
+		cd supersuper2 &&
+		git submodule update --init
+	) &&
+	git clone --recursive --reference-if-able supersuper2 supersuper2 supersuper-clone &&
+	(
+		cd supersuper-clone &&
+		# test superproject has alternates setup correctly
+		test_alternate_is_used .git/objects/info/alternates . &&
+		# update of the submodule fails
+		test_must_fail git submodule update --init --recursive &&
+		# immediate submodule has alternate:
+		test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub
+		# but nested submodule has no alternate:
+		test_must_fail test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
+	)
+'
+
 test_done
-- 
2.10.2


```

## Stefan Beller, 2016-12-07 20:09

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <CAGZ79kY3LR2KA69b4iDJb164EhJLb3JuVSRRcN0-4-kp-eryog@mail.gmail.com>
URL: https://gitlist.dev/e/CAGZ79kY3LR2KA69b4iDJb164EhJLb3JuVSRRcN0-4-kp-eryog%40mail.gmail.com
In-Reply-To: <20161207184248.6130-1-vi0oss@gmail.com>

```
On Wed, Dec 7, 2016 at 10:42 AM,  <vi0oss@gmail.com> wrote:
> From: Vitaly _Vi Shukela <vi0oss@gmail.com>

Thanks for contributing to Git!
(/me looks up if you have sent patches already as you
seem to know how to do that. :) unrelated side note: Maybe you want
to send a patch for the .mailmap file mapping your two email addresses
together, c.f. "git log -- .mailmap")

>
> Git v2.11 introduced "git clone --recursive --referece ...",
> but it didn't put the alternates for _nested_ submodules.

This message is targeted at people familiar with gits code base,
so we can be more specific. e.g.

    In 31224cbdc7 (clone: recursive and reference option triggers
    submodule alternates, 2016-08-17) a mechanism was added to
    have submodules referenced.  It did not address _nested_
    submodules, however.

>
> This patch makes all not just the root repository, but also
> all submodules (recursively) have submodule.alternateLocation
> and submodule.alternateErrorStrategy configured, making Git
> search for possible alternates for nested submodules as well.

Sounds great!

>
> As submodule's alternate target does not end in .git/objects
> (rather .git/modules/qqqqqq/objects), this alternate target
> path restriction for in add_possible_reference_from_superproject
> relates from "*.git/objects" to just */objects".

I wonder if this check is too weak and we actually have to check for
either .git/objects or modules/<name/possibly/having/slashes>/objects.
When writing the referenced commit I assumed we'd need a stronger check
to be safer and not add some random location as a possible alternate.

>
> New tests have been added to t7408-submodule-reference.

Thanks!

>
> Signed-off-by: Vitaly _Vi Shukela <vi0oss@gmail.com>
> ---
>  builtin/submodule--helper.c    | 24 ++++++++++++--
>  t/t7408-submodule-reference.sh | 73 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 95 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 4beeda5..93dae62 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -498,9 +498,9 @@ static int add_possible_reference_from_superproject(
>
>         /*
>          * If the alternate object store is another repository, try the
> -        * standard layout with .git/modules/<name>/objects
> +        * standard layout with .git/(modules/<name>)+/objects
>          */
> -       if (ends_with(alt->path, ".git/objects")) {
> +       if (ends_with(alt->path, "/objects")) {
>                 char *sm_alternate;
>                 struct strbuf sb = STRBUF_INIT;
>                 struct strbuf err = STRBUF_INIT;
> @@ -672,6 +672,26 @@ static int module_clone(int argc, const char **argv, const char *prefix)
>                 die(_("could not get submodule directory for '%s'"), path);
>         git_config_set_in_file(p, "core.worktree",
>                                relative_path(path, sm_gitdir, &rel_path));
> +
> +       /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
> +       {

Usually we do not use braces to further nest code, please remove this nesting.

> +               char *sm_alternate = NULL, *error_strategy = NULL;
> +
> +               git_config_get_string("submodule.alternateLocation", &sm_alternate);
> +               if (sm_alternate) {
> +                       git_config_set_in_file(p, "submodule.alternateLocation",
> +                                                  sm_alternate);
> +               }
> +               git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
> +               if (error_strategy) {
> +                       git_config_set_in_file(p, "submodule.alternateErrorStrategy",
> +                                                  error_strategy);
> +               }
> +
> +               free(sm_alternate);
> +               free(error_strategy);
> +       }
> +
>         strbuf_release(&sb);
>         strbuf_release(&rel_path);
>         free(sm_gitdir);
> diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
> index 1c1e289..7b64725 100755
> --- a/t/t7408-submodule-reference.sh
> +++ b/t/t7408-submodule-reference.sh
> @@ -125,4 +125,77 @@ test_expect_success 'ignoring missing submodule alternates passes clone and subm
>         )
>  '
>
> +test_expect_success 'preparing second superproject with a nested submodule' '
> +       test_create_repo supersuper &&
> +       (
> +               cd supersuper &&
> +               echo I am super super. >file &&

Usually we quote strings containing white space, e.g. echo "I am ..." >actual

> +               git add file &&
> +               git commit -m B-super-super-initial
> +               git submodule add "file://$base_dir/super" subwithsub &&
> +               git commit -m B-super-super-added &&
> +               git submodule update --init --recursive &&
> +               git repack -ad
> +       ) &&
> +       echo not cleaning supersuper

This echo is left in for debugging purposes?

```

## Junio C Hamano, 2016-12-07 20:18

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <xmqq4m2fxzl2.fsf@gitster.mtv.corp.google.com>
URL: https://gitlist.dev/e/xmqq4m2fxzl2.fsf%40gitster.mtv.corp.google.com
In-Reply-To: <CAGZ79kY3LR2KA69b4iDJb164EhJLb3JuVSRRcN0-4-kp-eryog@mail.gmail.com>

```
Stefan Beller <sbeller@google.com> writes:

>> This patch makes all not just the root repository, but also
>> all submodules (recursively) have submodule.alternateLocation
>> and submodule.alternateErrorStrategy configured, making Git
>> search for possible alternates for nested submodules as well.
>
> Sounds great!

Is it safe to assume that all the submodules used recursively by
submodules share the same structure upstream?  Does the alternate
location mechanism degrades sensibly if this assumption turns out to
be false (i.e. "possible alternates" above turns out to be mere
possibility and not there)?

```

## Stefan Beller, 2016-12-07 20:26

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <CAGZ79ka=XgO-VyS+Jqq2Fy28kPMy6HnXBAHb79Dt3NegZFd6kw@mail.gmail.com>
URL: https://gitlist.dev/e/CAGZ79ka%3DXgO-VyS%2BJqq2Fy28kPMy6HnXBAHb79Dt3NegZFd6kw%40mail.gmail.com
In-Reply-To: <xmqq4m2fxzl2.fsf@gitster.mtv.corp.google.com>

```
On Wed, Dec 7, 2016 at 12:18 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Stefan Beller <sbeller@google.com> writes:
>
>>> This patch makes all not just the root repository, but also
>>> all submodules (recursively) have submodule.alternateLocation
>>> and submodule.alternateErrorStrategy configured, making Git
>>> search for possible alternates for nested submodules as well.
>>
>> Sounds great!
>
> Is it safe to assume that all the submodules used recursively by
> submodules share the same structure upstream?  Does the alternate
> location mechanism degrades sensibly if this assumption turns out to
> be false (i.e. "possible alternates" above turns out to be mere
> possibility and not there)?

According to the last test in the patch, this seems to be doing the
sensible thing.

```

## Junio C Hamano, 2016-12-07 20:28

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <xmqqzik7wkj9.fsf@gitster.mtv.corp.google.com>
URL: https://gitlist.dev/e/xmqqzik7wkj9.fsf%40gitster.mtv.corp.google.com
In-Reply-To: <CAGZ79ka=XgO-VyS+Jqq2Fy28kPMy6HnXBAHb79Dt3NegZFd6kw@mail.gmail.com>

```
Stefan Beller <sbeller@google.com> writes:

> On Wed, Dec 7, 2016 at 12:18 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Stefan Beller <sbeller@google.com> writes:
>>
>>>> This patch makes all not just the root repository, but also
>>>> all submodules (recursively) have submodule.alternateLocation
>>>> and submodule.alternateErrorStrategy configured, making Git
>>>> search for possible alternates for nested submodules as well.
>>>
>>> Sounds great!
>>
>> Is it safe to assume that all the submodules used recursively by
>> submodules share the same structure upstream?  Does the alternate
>> location mechanism degrades sensibly if this assumption turns out to
>> be false (i.e. "possible alternates" above turns out to be mere
>> possibility and not there)?
>
> According to the last test in the patch, this seems to be doing the
> sensible thing.

OK, that sounds great.  Thanks.

```

## vi0oss, 2016-12-07 21:24

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <9493435c-16e5-d8af-9455-ec3f3cba390d@gmail.com>
URL: https://gitlist.dev/e/9493435c-16e5-d8af-9455-ec3f3cba390d%40gmail.com
In-Reply-To: <CAGZ79kY3LR2KA69b4iDJb164EhJLb3JuVSRRcN0-4-kp-eryog@mail.gmail.com>

```
On 12/07/2016 11:09 PM, Stefan Beller wrote:
>> As submodule's alternate target does not end in .git/objects
>> (rather .git/modules/qqqqqq/objects), this alternate target
>> path restriction for in add_possible_reference_from_superproject
>> relates from "*.git/objects" to just */objects".
> I wonder if this check is too weak and we actually have to check for
> either .git/objects or modules/<name/possibly/having/slashes>/objects.
> When writing the referenced commit I assumed we'd need a stronger check
> to be safer and not add some random location as a possible alternate.
>
1. Do we really need to check that it is named ".git"? Although
"git clone --mirror --recursive" is not (directly) supported
now, user may create one bare repository with [remnants of]
submodules by converting reqular repository into bare one.
Why not take advantage of additional information available locally
in this case?

2. Is the check need to be strict because of we need to traverse
one directory level up? Normally this "/objects" part is added by
Git, so just one "../" seems to be OK. User can't specify "--reference
somerepo/.git/objects", a strange reference can appear only if user
manually creates alternates. Maybe better to document this case
instead of restricting the feature?

3. If nonetheless check for ".git/*/objects", then
a. What functions should be used in Git codebase for such checks?
b. Should we handle tricks like "smth/.git/../../objects" and so on?

4. Should we print (or print in verbose mode) each used alternate,
to inform operator what his or her new clone will depend on?

P.S. Actually I discovered the --recursive --reference feature when tried to
put reference to a mega-repo with all possible submodules added as remotes.
I expected --reference to just get though across all submodules, but it 
complained
to missing "/modules/..." instead (the check went though becase the 
repository
was named like "megarepo.git", so it did ended in ".git/objects").

```

## Stefan Beller, 2016-12-07 22:09

Subject: Re: [PATCH] submodule--helper: set alternateLocation for cloned submodules
Message-ID: <CAGZ79kYuNri2aVtvRESxA9=E5OuBBqVkU_TcekxdyNJobFn5Mg@mail.gmail.com>
URL: https://gitlist.dev/e/CAGZ79kYuNri2aVtvRESxA9%3DE5OuBBqVkU_TcekxdyNJobFn5Mg%40mail.gmail.com
In-Reply-To: <9493435c-16e5-d8af-9455-ec3f3cba390d@gmail.com>

```
On Wed, Dec 7, 2016 at 1:24 PM, vi0oss <vi0oss@gmail.com> wrote:
> On 12/07/2016 11:09 PM, Stefan Beller wrote:
>>>
>>> As submodule's alternate target does not end in .git/objects
>>> (rather .git/modules/qqqqqq/objects), this alternate target
>>> path restriction for in add_possible_reference_from_superproject
>>> relates from "*.git/objects" to just */objects".
>>
>> I wonder if this check is too weak and we actually have to check for
>> either .git/objects or modules/<name/possibly/having/slashes>/objects.
>> When writing the referenced commit I assumed we'd need a stronger check
>> to be safer and not add some random location as a possible alternate.
>>
> 1. Do we really need to check that it is named ".git"? Although
> "git clone --mirror --recursive" is not (directly) supported
> now, user may create one bare repository with [remnants of]
> submodules by converting reqular repository into bare one.
> Why not take advantage of additional information available locally
> in this case?

Oh, great point.

>
> 2. Is the check need to be strict because of we need to traverse
> one directory level up? Normally this "/objects" part is added by
> Git, so just one "../" seems to be OK. User can't specify "--reference
> somerepo/.git/objects", a strange reference can appear only if user
> manually creates alternates. Maybe better to document this case
> instead of restricting the feature?

Not sure I understand what needs better documentation here?

>
> 3. If nonetheless check for ".git/*/objects", then
> a. What functions should be used in Git codebase for such checks?
> b. Should we handle tricks like "smth/.git/../../objects" and so on?

I see we're getting into problems here.

>
> 4. Should we print (or print in verbose mode) each used alternate,
> to inform operator what his or her new clone will depend on?
>
> P.S. Actually I discovered the --recursive --reference feature when tried to
> put reference to a mega-repo with all possible submodules added as remotes.
> I expected --reference to just get though across all submodules, but it
> complained
> to missing "/modules/..." instead (the check went though becase the
> repository
> was named like "megarepo.git", so it did ended in ".git/objects").

Oh :(

With that said, I think the original patch is a sensible approach.

Thanks,
Stefan

```
