Skip to content

Commit e2842b3

Browse files
jonathantanmygitster
authored andcommitted
fetch-pack: unify ref in and out param
When a user fetches: - at least one up-to-date ref and at least one non-up-to-date ref, - using HTTP with protocol v0 (or something else that uses the fetch command of a remote helper) some refs might not be updated after the fetch. This bug was introduced in commit 989b8c4 ("fetch-pack: put shallow info in output parameter", 2018-06-28) which allowed transports to report the refs that they have fetched in a new out-parameter "fetched_refs". If they do so, transport_fetch_refs() makes this information available to its caller. Users of "fetched_refs" rely on the following 3 properties: (1) it is the complete list of refs that was passed to transport_fetch_refs(), (2) it has shallow information (REF_STATUS_REJECT_SHALLOW set if relevant), and (3) it has updated OIDs if ref-in-want was used (introduced after 989b8c4). In an effort to satisfy (1), whenever transport_fetch_refs() filters the refs sent to the transport, it re-adds the filtered refs to whatever the transport supplies before returning it to the user. However, the implementation in 989b8c4 unconditionally re-adds the filtered refs without checking if the transport refrained from reporting anything in "fetched_refs" (which it is allowed to do), resulting in an incomplete list, no longer satisfying (1). An earlier effort to resolve this [1] solved the issue by readding the filtered refs only if the transport did not refrain from reporting in "fetched_refs", but after further discussion, it seems that the better solution is to revert the API change that introduced "fetched_refs". This API change was first suggested as part of a ref-in-want implementation that allowed for ref patterns and, thus, there could be drastic differences between the input refs and the refs actually fetched [2]; we eventually decided to only allow exact ref names, but this API change remained even though its necessity was decreased. Therefore, revert this API change by reverting commit 989b8c4, and make receive_wanted_refs() update the OIDs in the sought array (like how update_shallow() updates shallow information in the sought array) instead. A test is also included to show that the user-visible bug discussed at the beginning of this commit message no longer exists. [1] https://public-inbox.org/git/[email protected]/ [2] https://public-inbox.org/git/86a128c5fb710a41791e7183207c4d64889f9307.1485381677.git.jonathantanmy@google.com/ Signed-off-by: Jonathan Tan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cf1e7c0 commit e2842b3

File tree

9 files changed

+50
-84
lines changed

9 files changed

+50
-84
lines changed

builtin/clone.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11551155
}
11561156

11571157
if (!is_local && !complete_refs_before_fetch)
1158-
transport_fetch_refs(transport, mapped_refs, NULL);
1158+
transport_fetch_refs(transport, mapped_refs);
11591159

11601160
remote_head = find_ref_by_name(refs, "HEAD");
11611161
remote_head_points_at =
@@ -1197,7 +1197,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11971197
if (is_local)
11981198
clone_local(path, git_dir);
11991199
else if (refs && complete_refs_before_fetch)
1200-
transport_fetch_refs(transport, mapped_refs, NULL);
1200+
transport_fetch_refs(transport, mapped_refs);
12011201

12021202
update_remote_refs(refs, mapped_refs, remote_head_points_at,
12031203
branch_top.buf, reflog_msg.buf, transport,

builtin/fetch.c

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -948,13 +948,11 @@ static int quickfetch(struct ref *ref_map)
948948
return check_connected(iterate_ref_map, &rm, &opt);
949949
}
950950

951-
static int fetch_refs(struct transport *transport, struct ref *ref_map,
952-
struct ref **updated_remote_refs)
951+
static int fetch_refs(struct transport *transport, struct ref *ref_map)
953952
{
954953
int ret = quickfetch(ref_map);
955954
if (ret)
956-
ret = transport_fetch_refs(transport, ref_map,
957-
updated_remote_refs);
955+
ret = transport_fetch_refs(transport, ref_map);
958956
if (!ret)
959957
/*
960958
* Keep the new pack's ".keep" file around to allow the caller
@@ -1119,7 +1117,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
11191117
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
11201118
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
11211119
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
1122-
if (!fetch_refs(transport, ref_map, NULL))
1120+
if (!fetch_refs(transport, ref_map))
11231121
consume_refs(transport, ref_map);
11241122

11251123
if (gsecondary) {
@@ -1135,7 +1133,6 @@ static int do_fetch(struct transport *transport,
11351133
int autotags = (transport->remote->fetch_tags == 1);
11361134
int retcode = 0;
11371135
const struct ref *remote_refs;
1138-
struct ref *updated_remote_refs = NULL;
11391136
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
11401137

11411138
if (tags == TAGS_DEFAULT) {
@@ -1186,24 +1183,7 @@ static int do_fetch(struct transport *transport,
11861183
transport->url);
11871184
}
11881185
}
1189-
1190-
if (fetch_refs(transport, ref_map, &updated_remote_refs)) {
1191-
free_refs(ref_map);
1192-
retcode = 1;
1193-
goto cleanup;
1194-
}
1195-
if (updated_remote_refs) {
1196-
/*
1197-
* Regenerate ref_map using the updated remote refs. This is
1198-
* to account for additional information which may be provided
1199-
* by the transport (e.g. shallow info).
1200-
*/
1201-
free_refs(ref_map);
1202-
ref_map = get_ref_map(transport->remote, updated_remote_refs, rs,
1203-
tags, &autotags);
1204-
free_refs(updated_remote_refs);
1205-
}
1206-
if (consume_refs(transport, ref_map)) {
1186+
if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) {
12071187
free_refs(ref_map);
12081188
retcode = 1;
12091189
goto cleanup;

fetch-object.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static void fetch_refs(const char *remote_name, struct ref *ref)
1919

2020
transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
2121
transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
22-
transport_fetch_refs(transport, ref, NULL);
22+
transport_fetch_refs(transport, ref);
2323
fetch_if_missing = original_fetch_if_missing;
2424
}
2525

fetch-pack.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,25 +1338,26 @@ static void receive_shallow_info(struct fetch_pack_args *args,
13381338
args->deepen = 1;
13391339
}
13401340

1341-
static void receive_wanted_refs(struct packet_reader *reader, struct ref *refs)
1341+
static void receive_wanted_refs(struct packet_reader *reader,
1342+
struct ref **sought, int nr_sought)
13421343
{
13431344
process_section_header(reader, "wanted-refs", 0);
13441345
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
13451346
struct object_id oid;
13461347
const char *end;
1347-
struct ref *r = NULL;
1348+
int i;
13481349

13491350
if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ')
13501351
die("expected wanted-ref, got '%s'", reader->line);
13511352

1352-
for (r = refs; r; r = r->next) {
1353-
if (!strcmp(end, r->name)) {
1354-
oidcpy(&r->old_oid, &oid);
1353+
for (i = 0; i < nr_sought; i++) {
1354+
if (!strcmp(end, sought[i]->name)) {
1355+
oidcpy(&sought[i]->old_oid, &oid);
13551356
break;
13561357
}
13571358
}
13581359

1359-
if (!r)
1360+
if (i == nr_sought)
13601361
die("unexpected wanted-ref: '%s'", reader->line);
13611362
}
13621363

@@ -1439,7 +1440,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
14391440
receive_shallow_info(args, &reader);
14401441

14411442
if (process_section_header(&reader, "wanted-refs", 1))
1442-
receive_wanted_refs(&reader, ref);
1443+
receive_wanted_refs(&reader, sought, nr_sought);
14431444

14441445
/* get the pack */
14451446
process_section_header(&reader, "packfile", 0);
@@ -1503,13 +1504,12 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
15031504
}
15041505

15051506
static void update_shallow(struct fetch_pack_args *args,
1506-
struct ref *refs,
1507+
struct ref **sought, int nr_sought,
15071508
struct shallow_info *si)
15081509
{
15091510
struct oid_array ref = OID_ARRAY_INIT;
15101511
int *status;
15111512
int i;
1512-
struct ref *r;
15131513

15141514
if (args->deepen && alternate_shallow_file) {
15151515
if (*alternate_shallow_file == '\0') { /* --unshallow */
@@ -1551,8 +1551,8 @@ static void update_shallow(struct fetch_pack_args *args,
15511551
remove_nonexistent_theirs_shallow(si);
15521552
if (!si->nr_ours && !si->nr_theirs)
15531553
return;
1554-
for (r = refs; r; r = r->next)
1555-
oid_array_append(&ref, &r->old_oid);
1554+
for (i = 0; i < nr_sought; i++)
1555+
oid_array_append(&ref, &sought[i]->old_oid);
15561556
si->ref = &ref;
15571557

15581558
if (args->update_shallow) {
@@ -1586,12 +1586,12 @@ static void update_shallow(struct fetch_pack_args *args,
15861586
* remote is also shallow, check what ref is safe to update
15871587
* without updating .git/shallow
15881588
*/
1589-
status = xcalloc(ref.nr, sizeof(*status));
1589+
status = xcalloc(nr_sought, sizeof(*status));
15901590
assign_shallow_commits_to_refs(si, NULL, status);
15911591
if (si->nr_ours || si->nr_theirs) {
1592-
for (r = refs, i = 0; r; r = r->next, i++)
1592+
for (i = 0; i < nr_sought; i++)
15931593
if (status[i])
1594-
r->status = REF_STATUS_REJECT_SHALLOW;
1594+
sought[i]->status = REF_STATUS_REJECT_SHALLOW;
15951595
}
15961596
free(status);
15971597
oid_array_clear(&ref);
@@ -1654,7 +1654,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
16541654
args->connectivity_checked = 1;
16551655
}
16561656

1657-
update_shallow(args, ref_cpy, &si);
1657+
update_shallow(args, sought, nr_sought, &si);
16581658
cleanup:
16591659
clear_shallow_info(&si);
16601660
return ref_cpy;

t/t5551-http-fetch-smart.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,24 @@ test_expect_success 'custom http headers' '
369369
submodule update sub
370370
'
371371

372+
test_expect_success 'using fetch command in remote-curl updates refs' '
373+
SERVER="$HTTPD_DOCUMENT_ROOT_PATH/twobranch" &&
374+
rm -rf "$SERVER" client &&
375+
376+
git init "$SERVER" &&
377+
test_commit -C "$SERVER" foo &&
378+
git -C "$SERVER" update-ref refs/heads/anotherbranch foo &&
379+
380+
git clone $HTTPD_URL/smart/twobranch client &&
381+
382+
test_commit -C "$SERVER" bar &&
383+
git -C client -c protocol.version=0 fetch &&
384+
385+
git -C "$SERVER" rev-parse master >expect &&
386+
git -C client rev-parse origin/master >actual &&
387+
test_cmp expect actual
388+
'
389+
372390
test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
373391
rm -rf clone &&
374392
echo "Set-Cookie: Foo=1" >cookies &&

transport-helper.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -651,16 +651,14 @@ static int connect_helper(struct transport *transport, const char *name,
651651
}
652652

653653
static int fetch(struct transport *transport,
654-
int nr_heads, struct ref **to_fetch,
655-
struct ref **fetched_refs)
654+
int nr_heads, struct ref **to_fetch)
656655
{
657656
struct helper_data *data = transport->data;
658657
int i, count;
659658

660659
if (process_connect(transport, 0)) {
661660
do_take_over(transport);
662-
return transport->vtable->fetch(transport, nr_heads, to_fetch,
663-
fetched_refs);
661+
return transport->vtable->fetch(transport, nr_heads, to_fetch);
664662
}
665663

666664
count = 0;

transport-internal.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,11 @@ struct transport_vtable {
3636
* Fetch the objects for the given refs. Note that this gets
3737
* an array, and should ignore the list structure.
3838
*
39-
* The transport *may* provide, in fetched_refs, the list of refs that
40-
* it fetched. If the transport knows anything about the fetched refs
41-
* that the caller does not know (for example, shallow status), it
42-
* should provide that list of refs and include that information in the
43-
* list.
44-
*
4539
* If the transport did not get hashes for refs in
4640
* get_refs_list(), it should set the old_sha1 fields in the
4741
* provided refs now.
4842
**/
49-
int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs,
50-
struct ref **fetched_refs);
43+
int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
5144

5245
/**
5346
* Push the objects and refs. Send the necessary objects, and

transport.c

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
151151
}
152152

153153
static int fetch_refs_from_bundle(struct transport *transport,
154-
int nr_heads, struct ref **to_fetch,
155-
struct ref **fetched_refs)
154+
int nr_heads, struct ref **to_fetch)
156155
{
157156
struct bundle_transport_data *data = transport->data;
158157
return unbundle(&data->header, data->fd,
@@ -288,8 +287,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
288287
}
289288

290289
static int fetch_refs_via_pack(struct transport *transport,
291-
int nr_heads, struct ref **to_fetch,
292-
struct ref **fetched_refs)
290+
int nr_heads, struct ref **to_fetch)
293291
{
294292
int ret = 0;
295293
struct git_transport_data *data = transport->data;
@@ -357,12 +355,8 @@ static int fetch_refs_via_pack(struct transport *transport,
357355
if (report_unmatched_refs(to_fetch, nr_heads))
358356
ret = -1;
359357

360-
if (fetched_refs)
361-
*fetched_refs = refs;
362-
else
363-
free_refs(refs);
364-
365358
free_refs(refs_tmp);
359+
free_refs(refs);
366360
free(dest);
367361
return ret;
368362
}
@@ -1222,31 +1216,19 @@ const struct ref *transport_get_remote_refs(struct transport *transport,
12221216
return transport->remote_refs;
12231217
}
12241218

1225-
int transport_fetch_refs(struct transport *transport, struct ref *refs,
1226-
struct ref **fetched_refs)
1219+
int transport_fetch_refs(struct transport *transport, struct ref *refs)
12271220
{
12281221
int rc;
12291222
int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
12301223
struct ref **heads = NULL;
1231-
struct ref *nop_head = NULL, **nop_tail = &nop_head;
12321224
struct ref *rm;
12331225

12341226
for (rm = refs; rm; rm = rm->next) {
12351227
nr_refs++;
12361228
if (rm->peer_ref &&
12371229
!is_null_oid(&rm->old_oid) &&
1238-
!oidcmp(&rm->peer_ref->old_oid, &rm->old_oid)) {
1239-
/*
1240-
* These need to be reported as fetched, but we don't
1241-
* actually need to fetch them.
1242-
*/
1243-
if (fetched_refs) {
1244-
struct ref *nop_ref = copy_ref(rm);
1245-
*nop_tail = nop_ref;
1246-
nop_tail = &nop_ref->next;
1247-
}
1230+
!oidcmp(&rm->peer_ref->old_oid, &rm->old_oid))
12481231
continue;
1249-
}
12501232
ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
12511233
heads[nr_heads++] = rm;
12521234
}
@@ -1264,11 +1246,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs,
12641246
heads[nr_heads++] = rm;
12651247
}
12661248

1267-
rc = transport->vtable->fetch(transport, nr_heads, heads, fetched_refs);
1268-
if (fetched_refs && nop_head) {
1269-
*nop_tail = *fetched_refs;
1270-
*fetched_refs = nop_head;
1271-
}
1249+
rc = transport->vtable->fetch(transport, nr_heads, heads);
12721250

12731251
free(heads);
12741252
return rc;

transport.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ int transport_push(struct transport *connection,
229229
const struct ref *transport_get_remote_refs(struct transport *transport,
230230
const struct argv_array *ref_prefixes);
231231

232-
int transport_fetch_refs(struct transport *transport, struct ref *refs,
233-
struct ref **fetched_refs);
232+
int transport_fetch_refs(struct transport *transport, struct ref *refs);
234233
void transport_unlock_pack(struct transport *transport);
235234
int transport_disconnect(struct transport *transport);
236235
char *transport_anonymize_url(const char *url);

0 commit comments

Comments
 (0)