Skip to content

Commit 515be83

Browse files
szedergitster
authored andcommitted
clone: respect additional configured fetch refspecs during initial fetch
The initial fetch during a clone doesn't transfer refs matching additional fetch refspecs given on the command line as configuration variables, e.g. '-c remote.origin.fetch=<refspec>'. This contradicts the documentation stating that configuration variables specified via 'git clone -c <key>=<value> ...' "take effect immediately after the repository is initialized, but before the remote history is fetched" and the given example specifically mentions "adding additional fetch refspecs to the origin remote". Furthermore, one-shot configuration variables specified via 'git -c <key>=<value> clone ...', though not written to the newly created repository's config file, live during the lifetime of the 'clone' command, including the initial fetch. All this implies that any fetch refspecs specified this way should already be taken into account during the initial fetch. The reason for this is that the initial fetch is not a fully fledged 'git fetch' but a bunch of direct calls into the fetch/transport machinery with clone's own refs-to-refspec matching logic, which bypasses parts of 'git fetch' processing configured fetch refspecs. This logic only considers a single default refspec, potentially influenced by options like '--single-branch' and '--mirror'. The configured refspecs are, however, already read and parsed properly when clone calls remote.c:remote_get(), but it never looks at the parsed refspecs in the resulting 'struct remote'. Modify clone to take the remote's configured fetch refspecs into account to retrieve all matching refs during the initial fetch. Note that we have to explicitly add the default fetch refspec to the remote's refspecs, because at that point the remote only includes the fetch refspecs specified on the command line. Add tests to check that refspecs given both via 'git clone -c ...' and 'git -c ... clone' retrieve all refs matching either the default or the additional refspecs, and that it works even when the user specifies an alternative remote name via '--origin=<name>'. Signed-off-by: SZEDER Gábor <[email protected]> Reviewed-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3e42cb3 commit 515be83

File tree

2 files changed

+62
-10
lines changed

2 files changed

+62
-10
lines changed

builtin/clone.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
548548
}
549549

550550
static struct ref *wanted_peer_refs(const struct ref *refs,
551-
struct refspec_item *refspec)
551+
struct refspec *refspec)
552552
{
553553
struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
554554
struct ref *local_refs = head;
@@ -569,13 +569,19 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
569569
warning(_("Could not find remote branch %s to clone."),
570570
option_branch);
571571
else {
572-
get_fetch_map(remote_head, refspec, &tail, 0);
572+
int i;
573+
for (i = 0; i < refspec->nr; i++)
574+
get_fetch_map(remote_head, &refspec->items[i],
575+
&tail, 0);
573576

574577
/* if --branch=tag, pull the requested tag explicitly */
575578
get_fetch_map(remote_head, tag_refspec, &tail, 0);
576579
}
577-
} else
578-
get_fetch_map(refs, refspec, &tail, 0);
580+
} else {
581+
int i;
582+
for (i = 0; i < refspec->nr; i++)
583+
get_fetch_map(refs, &refspec->items[i], &tail, 0);
584+
}
579585

580586
if (!option_mirror && !option_single_branch && !option_no_tags)
581587
get_fetch_map(refs, tag_refspec, &tail, 0);
@@ -898,7 +904,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
898904
int err = 0, complete_refs_before_fetch = 1;
899905
int submodule_progress;
900906

901-
struct refspec rs = REFSPEC_INIT_FETCH;
902907
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
903908

904909
fetch_if_missing = 0;
@@ -1080,11 +1085,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
10801085
if (option_required_reference.nr || option_optional_reference.nr)
10811086
setup_reference();
10821087

1088+
remote = remote_get(option_origin);
1089+
10831090
strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
10841091
branch_top.buf);
1085-
refspec_append(&rs, default_refspec.buf);
1092+
refspec_append(&remote->fetch, default_refspec.buf);
10861093

1087-
remote = remote_get(option_origin);
10881094
transport = transport_get(remote, remote->url[0]);
10891095
transport_set_verbosity(transport, option_verbosity, option_progress);
10901096
transport->family = family;
@@ -1139,7 +1145,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11391145

11401146

11411147
argv_array_push(&ref_prefixes, "HEAD");
1142-
refspec_ref_prefixes(&rs, &ref_prefixes);
1148+
refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
11431149
if (option_branch)
11441150
expand_ref_prefix(&ref_prefixes, option_branch);
11451151
if (!option_no_tags)
@@ -1148,7 +1154,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11481154
refs = transport_get_remote_refs(transport, &ref_prefixes);
11491155

11501156
if (refs) {
1151-
mapped_refs = wanted_peer_refs(refs, &rs.items[0]);
1157+
mapped_refs = wanted_peer_refs(refs, &remote->fetch);
11521158
/*
11531159
* transport_get_remote_refs() may return refs with null sha-1
11541160
* in mapped_refs (see struct transport->get_refs_list
@@ -1242,7 +1248,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
12421248
strbuf_release(&default_refspec);
12431249
junk_mode = JUNK_LEAVE_ALL;
12441250

1245-
refspec_clear(&rs);
12461251
argv_array_clear(&ref_prefixes);
12471252
return err;
12481253
}

t/t5611-clone-config.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,53 @@ test_expect_success 'clone -c config is available during clone' '
4545
test_cmp expect child/file
4646
'
4747

48+
test_expect_success 'clone -c remote.origin.fetch=<refspec> works' '
49+
rm -rf child &&
50+
git update-ref refs/grab/it refs/heads/master &&
51+
git update-ref refs/leave/out refs/heads/master &&
52+
git clone -c "remote.origin.fetch=+refs/grab/*:refs/grab/*" . child &&
53+
git -C child for-each-ref --format="%(refname)" >actual &&
54+
55+
cat >expect <<-\EOF &&
56+
refs/grab/it
57+
refs/heads/master
58+
refs/remotes/origin/HEAD
59+
refs/remotes/origin/master
60+
EOF
61+
test_cmp expect actual
62+
'
63+
64+
test_expect_success 'git -c remote.origin.fetch=<refspec> clone works' '
65+
rm -rf child &&
66+
git -c "remote.origin.fetch=+refs/grab/*:refs/grab/*" clone . child &&
67+
git -C child for-each-ref --format="%(refname)" >actual &&
68+
69+
cat >expect <<-\EOF &&
70+
refs/grab/it
71+
refs/heads/master
72+
refs/remotes/origin/HEAD
73+
refs/remotes/origin/master
74+
EOF
75+
test_cmp expect actual
76+
'
77+
78+
test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '
79+
rm -rf child &&
80+
git clone --origin=upstream \
81+
-c "remote.upstream.fetch=+refs/grab/*:refs/grab/*" \
82+
-c "remote.origin.fetch=+refs/leave/*:refs/leave/*" \
83+
. child &&
84+
git -C child for-each-ref --format="%(refname)" >actual &&
85+
86+
cat >expect <<-\EOF &&
87+
refs/grab/it
88+
refs/heads/master
89+
refs/remotes/upstream/HEAD
90+
refs/remotes/upstream/master
91+
EOF
92+
test_cmp expect actual
93+
'
94+
4895
# Tests for the hidden file attribute on windows
4996
is_hidden () {
5097
# Use the output of `attrib`, ignore the absolute path

0 commit comments

Comments
 (0)