Skip to content

Commit b3a8046

Browse files
pks-tgitster
authored andcommitted
fetch: make --atomic flag cover backfilling of tags
When fetching references from a remote we by default also fetch all tags which point into the history we have fetched. This is a separate step performed after updating local references because it requires us to walk over the history on the client-side to determine whether the remote has announced any tags which point to one of the fetched commits. This backfilling of tags isn't covered by the `--atomic` flag: right now, it only applies to the step where we update our local references. This is an oversight at the time the flag was introduced: its purpose is to either update all references or none, but right now we happily update local references even in the case where backfilling failed. Fix this by pulling up creation of the reference transaction such that we can pass the same transaction to both the code which updates local references and to the code which backfills tags. This allows us to only commit the transaction in case both actions succeed. Note that we also have to start passing the transaction into `find_non_local_tags()`: this function is responsible for finding all tags which we need to backfill. Right now, it will happily return tags which have already been updated with our local references. But when we use a single transaction for both local references and backfilling then it may happen that we try to queue the same reference update twice to the transaction, which consequently triggers a bug. We thus have to skip over any tags which have already been queued. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4f2ba2d commit b3a8046

File tree

2 files changed

+69
-34
lines changed

2 files changed

+69
-34
lines changed

builtin/fetch.c

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,19 @@ static void clear_item(struct refname_hash_entry *item)
349349
item->ignore = 1;
350350
}
351351

352+
353+
static void add_already_queued_tags(const char *refname,
354+
const struct object_id *old_oid,
355+
const struct object_id *new_oid,
356+
void *cb_data)
357+
{
358+
struct hashmap *queued_tags = cb_data;
359+
if (starts_with(refname, "refs/tags/") && new_oid)
360+
(void) refname_hash_add(queued_tags, refname, new_oid);
361+
}
362+
352363
static void find_non_local_tags(const struct ref *refs,
364+
struct ref_transaction *transaction,
353365
struct ref **head,
354366
struct ref ***tail)
355367
{
@@ -367,6 +379,16 @@ static void find_non_local_tags(const struct ref *refs,
367379
create_fetch_oidset(head, &fetch_oids);
368380

369381
for_each_ref(add_one_refname, &existing_refs);
382+
383+
/*
384+
* If we already have a transaction, then we need to filter out all
385+
* tags which have already been queued up.
386+
*/
387+
if (transaction)
388+
ref_transaction_for_each_queued_update(transaction,
389+
add_already_queued_tags,
390+
&existing_refs);
391+
370392
for (ref = refs; ref; ref = ref->next) {
371393
if (!starts_with(ref->name, "refs/tags/"))
372394
continue;
@@ -600,7 +622,7 @@ static struct ref *get_ref_map(struct remote *remote,
600622
/* also fetch all tags */
601623
get_fetch_map(remote_refs, tag_refspec, &tail, 0);
602624
else if (tags == TAGS_DEFAULT && *autotags)
603-
find_non_local_tags(remote_refs, &ref_map, &tail);
625+
find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
604626

605627
/* Now append any refs to be updated opportunistically: */
606628
*tail = orefs;
@@ -1083,12 +1105,12 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
10831105
"to avoid this check\n");
10841106

10851107
static int store_updated_refs(const char *raw_url, const char *remote_name,
1086-
int connectivity_checked, struct ref *ref_map,
1108+
int connectivity_checked,
1109+
struct ref_transaction *transaction, struct ref *ref_map,
10871110
struct fetch_head *fetch_head, struct worktree **worktrees)
10881111
{
10891112
int url_len, i, rc = 0;
10901113
struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
1091-
struct ref_transaction *transaction = NULL;
10921114
const char *what, *kind;
10931115
struct ref *rm;
10941116
char *url;
@@ -1110,14 +1132,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
11101132
}
11111133
}
11121134

1113-
if (atomic_fetch) {
1114-
transaction = ref_transaction_begin(&err);
1115-
if (!transaction) {
1116-
error("%s", err.buf);
1117-
goto abort;
1118-
}
1119-
}
1120-
11211135
prepare_format_display(ref_map);
11221136

11231137
/*
@@ -1233,14 +1247,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
12331247
}
12341248
}
12351249

1236-
if (!rc && transaction) {
1237-
rc = ref_transaction_commit(transaction, &err);
1238-
if (rc) {
1239-
error("%s", err.buf);
1240-
goto abort;
1241-
}
1242-
}
1243-
12441250
if (rc & STORE_REF_ERROR_DF_CONFLICT)
12451251
error(_("some local refs could not be updated; try running\n"
12461252
" 'git remote prune %s' to remove any old, conflicting "
@@ -1258,7 +1264,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
12581264
abort:
12591265
strbuf_release(&note);
12601266
strbuf_release(&err);
1261-
ref_transaction_free(transaction);
12621267
free(url);
12631268
return rc;
12641269
}
@@ -1299,6 +1304,7 @@ static int check_exist_and_connected(struct ref *ref_map)
12991304
}
13001305

13011306
static int fetch_and_consume_refs(struct transport *transport,
1307+
struct ref_transaction *transaction,
13021308
struct ref *ref_map,
13031309
struct fetch_head *fetch_head,
13041310
struct worktree **worktrees)
@@ -1323,7 +1329,8 @@ static int fetch_and_consume_refs(struct transport *transport,
13231329

13241330
trace2_region_enter("fetch", "consume_refs", the_repository);
13251331
ret = store_updated_refs(transport->url, transport->remote->name,
1326-
connectivity_checked, ref_map, fetch_head, worktrees);
1332+
connectivity_checked, transaction, ref_map,
1333+
fetch_head, worktrees);
13271334
trace2_region_leave("fetch", "consume_refs", the_repository);
13281335

13291336
out:
@@ -1496,6 +1503,7 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
14961503
}
14971504

14981505
static int backfill_tags(struct transport *transport,
1506+
struct ref_transaction *transaction,
14991507
struct ref *ref_map,
15001508
struct fetch_head *fetch_head,
15011509
struct worktree **worktrees)
@@ -1519,7 +1527,7 @@ static int backfill_tags(struct transport *transport,
15191527
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
15201528
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
15211529
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
1522-
retcode = fetch_and_consume_refs(transport, ref_map, fetch_head, worktrees);
1530+
retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head, worktrees);
15231531

15241532
if (gsecondary) {
15251533
transport_disconnect(gsecondary);
@@ -1532,6 +1540,7 @@ static int backfill_tags(struct transport *transport,
15321540
static int do_fetch(struct transport *transport,
15331541
struct refspec *rs)
15341542
{
1543+
struct ref_transaction *transaction = NULL;
15351544
struct ref *ref_map = NULL;
15361545
int autotags = (transport->remote->fetch_tags == 1);
15371546
int retcode = 0;
@@ -1541,6 +1550,7 @@ static int do_fetch(struct transport *transport,
15411550
int must_list_refs = 1;
15421551
struct worktree **worktrees = get_worktrees();
15431552
struct fetch_head fetch_head = { 0 };
1553+
struct strbuf err = STRBUF_INIT;
15441554

15451555
if (tags == TAGS_DEFAULT) {
15461556
if (transport->remote->fetch_tags == 2)
@@ -1602,6 +1612,14 @@ static int do_fetch(struct transport *transport,
16021612
if (retcode)
16031613
goto cleanup;
16041614

1615+
if (atomic_fetch) {
1616+
transaction = ref_transaction_begin(&err);
1617+
if (!transaction) {
1618+
retcode = error("%s", err.buf);
1619+
goto cleanup;
1620+
}
1621+
}
1622+
16051623
if (tags == TAGS_DEFAULT && autotags)
16061624
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
16071625
if (prune) {
@@ -1621,7 +1639,7 @@ static int do_fetch(struct transport *transport,
16211639
retcode = 1;
16221640
}
16231641

1624-
if (fetch_and_consume_refs(transport, ref_map, &fetch_head, worktrees)) {
1642+
if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head, worktrees)) {
16251643
retcode = 1;
16261644
goto cleanup;
16271645
}
@@ -1633,21 +1651,37 @@ static int do_fetch(struct transport *transport,
16331651
if (tags == TAGS_DEFAULT && autotags) {
16341652
struct ref *tags_ref_map = NULL, **tail = &tags_ref_map;
16351653

1636-
find_non_local_tags(remote_refs, &tags_ref_map, &tail);
1654+
find_non_local_tags(remote_refs, transaction, &tags_ref_map, &tail);
16371655
if (tags_ref_map) {
16381656
/*
16391657
* If backfilling of tags fails then we want to tell
16401658
* the user so, but we have to continue regardless to
16411659
* populate upstream information of the references we
1642-
* have already fetched above.
1660+
* have already fetched above. The exception though is
1661+
* when `--atomic` is passed: in that case we'll abort
1662+
* the transaction and don't commit anything.
16431663
*/
1644-
if (backfill_tags(transport, tags_ref_map, &fetch_head, worktrees))
1664+
if (backfill_tags(transport, transaction, tags_ref_map,
1665+
&fetch_head, worktrees))
16451666
retcode = 1;
16461667
}
16471668

16481669
free_refs(tags_ref_map);
16491670
}
16501671

1672+
if (transaction) {
1673+
if (retcode)
1674+
goto cleanup;
1675+
1676+
retcode = ref_transaction_commit(transaction, &err);
1677+
if (retcode) {
1678+
error("%s", err.buf);
1679+
ref_transaction_free(transaction);
1680+
transaction = NULL;
1681+
goto cleanup;
1682+
}
1683+
}
1684+
16511685
commit_fetch_head(&fetch_head);
16521686

16531687
if (set_upstream) {
@@ -1705,7 +1739,13 @@ static int do_fetch(struct transport *transport,
17051739
}
17061740

17071741
cleanup:
1742+
if (retcode && transaction) {
1743+
ref_transaction_abort(transaction, &err);
1744+
error("%s", err.buf);
1745+
}
1746+
17081747
close_fetch_head(&fetch_head);
1748+
strbuf_release(&err);
17091749
free_refs(ref_map);
17101750
free_worktrees(worktrees);
17111751
return retcode;

t/t5503-tagfollow.sh

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,8 @@ test_expect_success 'atomic fetch with failing backfill' '
180180
EOF
181181
182182
test_must_fail git -C clone3 fetch --atomic .. $B:refs/heads/something &&
183-
184-
# Creation of the tag has failed, so ideally refs/heads/something
185-
# should not exist. The fact that it does demonstrates that there is
186-
# a bug in the `--atomic` flag.
187-
test $B = "$(git -C clone3 rev-parse --verify refs/heads/something)"
183+
test_must_fail git -C clone3 rev-parse --verify refs/heads/something &&
184+
test_must_fail git -C clone3 rev-parse --verify refs/tags/tag2
188185
'
189186

190187
test_expect_success 'atomic fetch with backfill should use single transaction' '
@@ -197,12 +194,10 @@ test_expect_success 'atomic fetch with backfill should use single transaction' '
197194
prepared
198195
$ZERO_OID $B refs/heads/something
199196
$ZERO_OID $S refs/tags/tag2
197+
$ZERO_OID $T refs/tags/tag1
200198
committed
201199
$ZERO_OID $B refs/heads/something
202200
$ZERO_OID $S refs/tags/tag2
203-
prepared
204-
$ZERO_OID $T refs/tags/tag1
205-
committed
206201
$ZERO_OID $T refs/tags/tag1
207202
EOF
208203

0 commit comments

Comments
 (0)