Skip to content

Commit

Permalink
submodule helper: convert relative URL to absolute URL if needed
Browse files Browse the repository at this point in the history
The submodule helper update_clone called by "git submodule update",
clones submodules if needed. As submodules used to have the URL indicating
if they were active, the step to resolve relative URLs was done in the
"submodule init" step. Nowadays submodules can be configured active without
calling an explicit init, e.g. via configuring submodule.active.

When trying to obtain submodules that are set active this way, we'll
fallback to the URL found in the .gitmodules, which may be relative to the
superproject, but we do not resolve it, yet:

    git clone https://gerrit.googlesource.com/gerrit
    cd gerrit && grep url .gitmodules
	url = ../plugins/codemirror-editor
	...
    git config submodule.active .
    git submodule update
    fatal: repository '../plugins/codemirror-editor' does not exist
    fatal: clone of '../plugins/codemirror-editor' into submodule path '/tmp/gerrit/plugins/codemirror-editor' failed
    Failed to clone 'plugins/codemirror-editor'. Retry scheduled
    [...]
    fatal: clone of '../plugins/codemirror-editor' into submodule path '/tmp/gerrit/plugins/codemirror-editor' failed
    Failed to clone 'plugins/codemirror-editor' a second time, aborting
    [...]

To resolve the issue, factor out the function that resolves the relative
URLs in "git submodule init" (in the submodule helper in the init_submodule
function) and call it at the appropriate place in the update_clone helper.

Reported-by: Jaewoong Jung <jungjw@google.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
stefanbeller authored and gitster committed Oct 18, 2018
1 parent cae598d commit e0a862f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 17 deletions.
51 changes: 34 additions & 17 deletions builtin/submodule--helper.c
Expand Up @@ -584,6 +584,26 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
return 0;
}

static char *compute_submodule_clone_url(const char *rel_url)
{
char *remoteurl, *relurl;
char *remote = get_default_remote();
struct strbuf remotesb = STRBUF_INIT;

strbuf_addf(&remotesb, "remote.%s.url", remote);
if (git_config_get_string(remotesb.buf, &remoteurl)) {
warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
remoteurl = xgetcwd();
}
relurl = relative_url(remoteurl, rel_url, NULL);

free(remote);
free(remoteurl);
strbuf_release(&remotesb);

return relurl;
}

struct init_cb {
const char *prefix;
unsigned int flags;
Expand Down Expand Up @@ -634,21 +654,9 @@ static void init_submodule(const char *path, const char *prefix,
/* Possibly a url relative to parent */
if (starts_with_dot_dot_slash(url) ||
starts_with_dot_slash(url)) {
char *remoteurl, *relurl;
char *remote = get_default_remote();
struct strbuf remotesb = STRBUF_INIT;
strbuf_addf(&remotesb, "remote.%s.url", remote);
free(remote);

if (git_config_get_string(remotesb.buf, &remoteurl)) {
warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
remoteurl = xgetcwd();
}
relurl = relative_url(remoteurl, url, NULL);
strbuf_release(&remotesb);
free(remoteurl);
free(url);
url = relurl;
char *oldurl = url;
url = compute_submodule_clone_url(oldurl);
free(oldurl);
}

if (git_config_set_gently(sb.buf, url))
Expand Down Expand Up @@ -1515,6 +1523,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
struct strbuf sb = STRBUF_INIT;
const char *displaypath = NULL;
int needs_cloning = 0;
int need_free_url = 0;

if (ce_stage(ce)) {
if (suc->recursive_prefix)
Expand Down Expand Up @@ -1563,8 +1572,14 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,

strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
if (repo_config_get_string_const(the_repository, sb.buf, &url))
url = sub->url;
if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
if (starts_with_dot_slash(sub->url) ||
starts_with_dot_dot_slash(sub->url)) {
url = compute_submodule_clone_url(sub->url);
need_free_url = 1;
} else
url = sub->url;
}

strbuf_reset(&sb);
strbuf_addf(&sb, "%s/.git", ce->name);
Expand Down Expand Up @@ -1609,6 +1624,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
cleanup:
strbuf_reset(&displaypath_sb);
strbuf_reset(&sb);
if (need_free_url)
free((void*)url);

return needs_cloning;
}
Expand Down
24 changes: 24 additions & 0 deletions t/t7400-submodule-basic.sh
Expand Up @@ -1224,6 +1224,30 @@ test_expect_success 'submodule update and setting submodule.<name>.active' '
test_cmp expect actual
'

test_expect_success 'clone active submodule without submodule url set' '
test_when_finished "rm -rf test/test" &&
mkdir test &&
# another dir breaks accidental relative paths still being correct
git clone file://"$pwd"/multisuper test/test &&
(
cd test/test &&
git config submodule.active "." &&
# do not pass --init flag, as the submodule is already active:
git submodule update &&
git submodule status >actual_raw &&
cut -c 1,43- actual_raw >actual &&
cat >expect <<-\EOF &&
sub0 (test2)
sub1 (test2)
sub2 (test2)
sub3 (test2)
EOF
test_cmp expect actual
)
'

test_expect_success 'clone --recurse-submodules with a pathspec works' '
test_when_finished "rm -rf multisuper_clone" &&
cat >expected <<-\EOF &&
Expand Down

0 comments on commit e0a862f

Please sign in to comment.