threads / patch / 44648

patchsubmodule--helper: set alternateLocation for cloned submodules

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

## tl;dr

7 messages between Dec 8, 2016 and Dec 8, 2016. Diffs are folded; open one to read it.

replies: 6people: 3as markdown or json

vi0oss@gmail.com· Dec 8, 2016, 00:39 UTC · lore
From: "Vitaly \"_Vi\" Shukela" <vi0oss@gmail.com>

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.

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>
---
Notes:
    Another round of fixups:
    
    * Corrected code style
    * Refactored and fixed the test
    
    Previously test contained errorneous
    test_must_fail, which was masked by
    missing &&.
    
    Mailmap patch omitted this time.
 builtin/submodule--helper.c    | 19 ++++++++++--
 t/t7408-submodule-reference.sh | 66 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 2 deletions(-)
Show changes to 2 files +83 −2

builtin/submodule--helper.c, t/t7408-submodule-reference.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4beeda5..92fd676 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;
@@ -583,6 +583,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf rel_path = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct string_list reference = STRING_LIST_INIT_NODUP;
+	char *sm_alternate = NULL, *error_strategy = NULL;
 
 	struct option module_clone_options[] = {
 		OPT_STRING(0, "prefix", &prefix,
@@ -672,6 +673,20 @@ 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 */
+	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..9325297 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -125,4 +125,70 @@ test_expect_success 'ignoring missing submodule alternates passes clone and subm
 	)
 '
 
+test_expect_success 'preparing second superproject with a nested submodule plus partial clone' '
+	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
+	) &&
+	git clone supersuper supersuper2 &&
+	(
+		cd supersuper2 &&
+		git submodule update --init
+	)
+'
+
+# 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
+	)
+'
+
+check_that_two_of_three_alternates_are_used() {
+	test_alternate_is_used .git/objects/info/alternates . &&
+	# 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 fails clone and submodule update' '
+	test_when_finished "rm -rf supersuper-clone" &&
+	test_must_fail git clone --recursive --reference supersuper2 supersuper2 supersuper-clone &&
+	(
+		cd supersuper-clone &&
+		check_that_two_of_three_alternates_are_used &&
+		# update of the submodule fails
+		test_must_fail git submodule update --init --recursive
+	)
+'
+
+test_expect_success 'missing nested submodule alternate in --reference-if-able mode' '
+	test_when_finished "rm -rf supersuper-clone" &&
+	git clone --recursive --reference-if-able supersuper2 supersuper2 supersuper-clone &&
+	(
+		cd supersuper-clone &&
+		check_that_two_of_three_alternates_are_used &&
+		# update of the submodule succeeds
+		git submodule update --init --recursive
+	)
+'
+
 test_done
-- 
2.10.2
Stefan Beller· Dec 8, 2016, 01:22 UTC · re: vi0oss@gmail.com · lore

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

On Wed, Dec 7, 2016 at 4:39 PM,  <vi0oss@gmail.com> wrote:
>
>     Previously test contained errorneous
>     test_must_fail, which was masked by
>     missing &&.

I wonder if we could make either the test_must_fail intelligent to detect such a broken && call chain or the test_expect_success macro to see for those broken chains.

Patch looks good to me except one very minor nit. :)
Show 9 quoted lines
> +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
here is a && missing ;)
Jeff King· Dec 8, 2016, 17:46 UTC · re: Stefan Beller · lore

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

On Wed, Dec 07, 2016 at 05:22:30PM -0800, Stefan Beller wrote:
Show 10 quoted lines
> On Wed, Dec 7, 2016 at 4:39 PM,  <vi0oss@gmail.com> wrote:
> 
> >
> >     Previously test contained errorneous
> >     test_must_fail, which was masked by
> >     missing &&.
> 
> I wonder if we could make either
> the test_must_fail intelligent to detect such a broken && call chain
> or the test_expect_success macro to see for those broken chains.

I don't think test_must_fail is relevant for &&-chains. Even something like:

  test_must_fail foo
  bar
or:
  bar
  test_must_fail foo

will both trigger on the &&-chain linter, because it uses a magic exit code to detect the breakage. I think the problem is just that the &&-chain linter cannot peek inside subshells, and that's where the bug was in this case.

I wish we could improve that, but I spend a lot of brain cycles on it at one point and couldn't come up with a workable solution.

-Peff
Stefan Beller· Dec 8, 2016, 18:04 UTC · re: Jeff King · lore

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

On Thu, Dec 8, 2016 at 9:46 AM, Jeff King <peff@peff.net> wrote:
Show 5 quoted lines
>
> will both trigger on the &&-chain linter, because it uses a magic exit
> code to detect the breakage. I think the problem is just that the
> &&-chain linter cannot peek inside subshells, and that's where the bug
> was in this case.

Uh, yeah in the subshell, but the patch v2 did have it not in subshells, I'll take another look.

>
> I wish we could improve that, but I spend a lot of brain cycles on it at
> one point and couldn't come up with a workable solution.

Is it possible to overwrite what happens when you open a subshell with ( ) ? i.e. I imagine in the global test-setup we'd setup that ( ) not just opens /bin/sh but a shell with benefits such that we can execute just one && chain.

vi0oss· Dec 8, 2016, 18:04 UTC · re: Jeff King · lore

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

On 12/08/2016 08:46 PM, Jeff King wrote:
Show 17 quoted lines
> On Wed, Dec 07, 2016 at 05:22:30PM -0800, Stefan Beller wrote:
>
>> On Wed, Dec 7, 2016 at 4:39 PM,  <vi0oss@gmail.com> wrote:
>>
>>>      Previously test contained errorneous
>>>      test_must_fail, which was masked by
>>>      missing &&.
>> I wonder if we could make either
>> the test_must_fail intelligent to detect such a broken && call chain
>> or the test_expect_success macro to see for those broken chains.
>>
>>
>> I wish we could improve that, but I spend a lot of brain cycles on it at
>> one point and couldn't come up with a workable solution.
>>
>> -Peff
>>
Why Git test use &&-chains instead of proper "set -e"?
Stefan Beller· Dec 8, 2016, 18:21 UTC · re: vi0oss · lore

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

On Thu, Dec 8, 2016 at 10:04 AM, vi0oss <vi0oss@gmail.com> wrote:
Show 22 quoted lines
> On 12/08/2016 08:46 PM, Jeff King wrote:
>>
>> On Wed, Dec 07, 2016 at 05:22:30PM -0800, Stefan Beller wrote:
>>
>>> On Wed, Dec 7, 2016 at 4:39 PM,  <vi0oss@gmail.com> wrote:
>>>
>>>>      Previously test contained errorneous
>>>>      test_must_fail, which was masked by
>>>>      missing &&.
>>>
>>> I wonder if we could make either
>>> the test_must_fail intelligent to detect such a broken && call chain
>>> or the test_expect_success macro to see for those broken chains.
>>>
>>>
>>> I wish we could improve that, but I spend a lot of brain cycles on it at
>>> one point and couldn't come up with a workable solution.
>>>
>>> -Peff
>>>
> Why Git test use &&-chains instead of proper "set -e"?
>

"Because set -e kills the shell and we would want to keep going until the test suite is finished and display a summary what failed" would be my first reaction, but let's dig into history: bb79af9d09 might be helpful on that, but it doesn't explain why we use && chains. I could not find any commit explaining the use of && chains.

e1970ce43abf might be interesting (the introduction of the test suite), as that did not contain && chains.

I guess it would be hard(er) to implement e.g. test_must_fail in an environment where -e is set.

Jeff King· Dec 8, 2016, 18:53 UTC · re: vi0oss · lore

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

On Thu, Dec 08, 2016 at 09:04:46PM +0300, vi0oss wrote:
> Why Git test use &&-chains instead of proper "set -e"?

Because "set -e" comes with all kinds of confusing corner cases. Using && chains is annoying, but rarely surprising.

One of my favorite examples is:
  set -e
  (
    false
    echo 1
  ) || {
    echo outcome=$?
    false
  }
  echo 2
which prints both "1" and "2".

Inside the subshell, "set -e" has no effect, and you cannot re-enable it by setting "-e" (it's suppressed entirely because we are on the left-hand side of an || conditional).

So you could write a function like this:
  foo() {
    do_one
    do_two
  }
that relies on catching the failure from do_one. And it works here:
  set -e
  foo
but not here:
  set -e
  if foo then
    do_something
  fi

And there's no way to make it work without adding back in the &&-chaining.

-Peff

← back to recent threads