Skip to content

Commit cea257d

Browse files
jacob-kellergitster
authored andcommitted
submodule: look up remotes by URL first
The get_default_remote_submodule() function performs a lookup to find the appropriate remote to use within a submodule. The function first checks to see if it can find the remote for the current branch. If this fails, it then checks to see if there is exactly one remote. It will use this, before finally falling back to "origin" as the default. If a user happens to rename their default remote from origin, either manually or by setting something like clone.defaultRemoteName, this fallback will not work. In such cases, the submodule logic will try to use a non-existent remote. This usually manifests as a failure to trigger the submodule update. The parent project already knows and stores the submodule URL in either .gitmodules or its .git/config. Add a new repo_remote_from_url() helper which will iterate over all the remotes in a repository and return the first remote which has a matching URL. Refactor get_default_remote_submodule to find the submodule and get its URL. If a valid URL exists, first try to obtain a remote using the new repo_remote_from_url(). Fall back to the repo_default_remote() otherwise. The fallback logic is kept in case for some reason the user has manually changed the URL within the submodule. Additionally, we still try to use a remote rather than directly passing the URL in the fetch_in_submodule() logic. This ensures that an update will properly update the remote refs within the submodule as expected, rather than just fetching into FETCH_HEAD. Signed-off-by: Jacob Keller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b645f8f commit cea257d

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

builtin/submodule--helper.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,40 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int
7272

7373
static int get_default_remote_submodule(const char *module_path, char **default_remote)
7474
{
75+
const struct submodule *sub;
7576
struct repository subrepo;
77+
const char *remote_name = NULL;
78+
char *url = NULL;
79+
80+
sub = submodule_from_path(the_repository, null_oid(the_hash_algo), module_path);
81+
if (sub && sub->url) {
82+
url = xstrdup(sub->url);
83+
84+
/* Possibly a url relative to parent */
85+
if (starts_with_dot_dot_slash(url) ||
86+
starts_with_dot_slash(url)) {
87+
char *oldurl = url;
88+
89+
url = resolve_relative_url(oldurl, NULL, 1);
90+
free(oldurl);
91+
}
92+
}
7693

7794
if (repo_submodule_init(&subrepo, the_repository, module_path,
7895
null_oid(the_hash_algo)) < 0)
7996
return die_message(_("could not get a repository handle for submodule '%s'"),
8097
module_path);
8198

82-
*default_remote = xstrdup(repo_default_remote(&subrepo));
99+
/* Look up by URL first */
100+
if (url)
101+
remote_name = repo_remote_from_url(&subrepo, url);
102+
if (!remote_name)
103+
remote_name = repo_default_remote(&subrepo);
104+
105+
*default_remote = xstrdup(remote_name);
83106

84107
repo_clear(&subrepo);
108+
free(url);
85109

86110
return 0;
87111
}

remote.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,21 @@ const char *repo_default_remote(struct repository *repo)
18011801
return remotes_remote_for_branch(repo->remote_state, branch, NULL);
18021802
}
18031803

1804+
const char *repo_remote_from_url(struct repository *repo, const char *url)
1805+
{
1806+
read_config(repo, 0);
1807+
1808+
for (int i = 0; i < repo->remote_state->remotes_nr; i++) {
1809+
struct remote *remote = repo->remote_state->remotes[i];
1810+
if (!remote)
1811+
continue;
1812+
1813+
if (remote_has_url(remote, url))
1814+
return remote->name;
1815+
}
1816+
return NULL;
1817+
}
1818+
18041819
int branch_has_merge_config(struct branch *branch)
18051820
{
18061821
return branch && !!branch->merge;

remote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit);
340340
char *remote_ref_for_branch(struct branch *branch, int for_push);
341341

342342
const char *repo_default_remote(struct repository *repo);
343+
const char *repo_remote_from_url(struct repository *repo, const char *url);
343344

344345
/* returns true if the given branch has merge configuration given. */
345346
int branch_has_merge_config(struct branch *branch);

t/t7406-submodule-update.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,38 @@ test_expect_success 'setup clean recursive superproject' '
11341134
git clone --recurse-submodules top top-clean
11351135
'
11361136

1137+
test_expect_success 'submodule update with multiple remotes' '
1138+
test_when_finished "rm -fr top-cloned" &&
1139+
cp -r top-clean top-cloned &&
1140+
1141+
# Create a commit in each repo, starting with bottom
1142+
test_commit -C bottom multiple_remote_commit &&
1143+
# Create middle commit
1144+
git -C middle/bottom fetch &&
1145+
git -C middle/bottom checkout -f FETCH_HEAD &&
1146+
git -C middle add bottom &&
1147+
git -C middle commit -m "multiple_remote_commit" &&
1148+
# Create top commit
1149+
git -C top/middle fetch &&
1150+
git -C top/middle checkout -f FETCH_HEAD &&
1151+
git -C top add middle &&
1152+
git -C top commit -m "multiple_remote_commit" &&
1153+
1154+
# rename the submodule remote
1155+
git -C top-cloned/middle remote rename origin upstream &&
1156+
1157+
# Add another remote
1158+
git -C top-cloned/middle remote add other bogus &&
1159+
1160+
# Make the update of "middle" a no-op, otherwise we error out
1161+
# because of its unmerged state
1162+
test_config -C top-cloned submodule.middle.update !true &&
1163+
git -C top-cloned submodule update --recursive 2>actual.err &&
1164+
cat >expect.err <<-\EOF &&
1165+
EOF
1166+
test_cmp expect.err actual.err
1167+
'
1168+
11371169
test_expect_success 'submodule update with renamed remote' '
11381170
test_when_finished "rm -fr top-cloned" &&
11391171
cp -r top-clean top-cloned &&

0 commit comments

Comments
 (0)