Skip to content

Commit 761e62a

Browse files
committed
Merge branch 'bf/set-head-symref' into bf/fetch-set-head-config
* bf/set-head-symref: fetch set_head: handle mirrored bare repositories fetch: set remote/HEAD if it does not exist refs: add create_only option to refs_update_symref_extended refs: add TRANSACTION_CREATE_EXISTS error remote set-head: better output for --auto remote set-head: refactor for readability refs: atomically record overwritten ref in update_symref refs: standardize output of refs_read_symbolic_ref t/t5505-remote: test failure of set-head t/t5505-remote: set default branch to main
2 parents cc01bad + b1b713f commit 761e62a

18 files changed

+439
-59
lines changed

builtin/fetch.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,72 @@ static int backfill_tags(struct display_state *display_state,
15741574
return retcode;
15751575
}
15761576

1577+
static const char *strip_refshead(const char *name){
1578+
skip_prefix(name, "refs/heads/", &name);
1579+
return name;
1580+
}
1581+
1582+
static int set_head(const struct ref *remote_refs)
1583+
{
1584+
int result = 0, is_bare;
1585+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT;
1586+
const char *remote = gtransport->remote->name;
1587+
char *head_name = NULL;
1588+
struct ref *ref, *matches;
1589+
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
1590+
struct refspec_item refspec = {
1591+
.force = 0,
1592+
.pattern = 1,
1593+
.src = (char *) "refs/heads/*",
1594+
.dst = (char *) "refs/heads/*",
1595+
};
1596+
struct string_list heads = STRING_LIST_INIT_DUP;
1597+
struct ref_store *refs = get_main_ref_store(the_repository);
1598+
1599+
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
1600+
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
1601+
fetch_map, 1);
1602+
for (ref = matches; ref; ref = ref->next) {
1603+
string_list_append(&heads, strip_refshead(ref->name));
1604+
}
1605+
1606+
1607+
if (!heads.nr)
1608+
result = 1;
1609+
else if (heads.nr > 1)
1610+
result = 1;
1611+
else
1612+
head_name = xstrdup(heads.items[0].string);
1613+
1614+
if (!head_name)
1615+
goto cleanup;
1616+
is_bare = is_bare_repository();
1617+
if (is_bare) {
1618+
strbuf_addstr(&b_head, "HEAD");
1619+
strbuf_addf(&b_remote_head, "refs/heads/%s", head_name);
1620+
} else {
1621+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote);
1622+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name);
1623+
}
1624+
/* make sure it's valid */
1625+
if (!is_bare && !refs_ref_exists(refs, b_remote_head.buf)) {
1626+
result = 1;
1627+
goto cleanup;
1628+
}
1629+
if (refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1630+
"fetch", NULL, !is_bare))
1631+
result = 1;
1632+
1633+
cleanup:
1634+
free(head_name);
1635+
free_refs(fetch_map);
1636+
free_refs(matches);
1637+
string_list_clear(&heads, 0);
1638+
strbuf_release(&b_head);
1639+
strbuf_release(&b_remote_head);
1640+
return result;
1641+
}
1642+
15771643
static int do_fetch(struct transport *transport,
15781644
struct refspec *rs,
15791645
const struct fetch_config *config)
@@ -1643,6 +1709,8 @@ static int do_fetch(struct transport *transport,
16431709
"refs/tags/");
16441710
}
16451711

1712+
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1713+
16461714
if (must_list_refs) {
16471715
trace2_region_enter("fetch", "remote_refs", the_repository);
16481716
remote_refs = transport_get_remote_refs(transport,
@@ -1787,6 +1855,12 @@ static int do_fetch(struct transport *transport,
17871855
"you need to specify exactly one branch with the --set-upstream option"));
17881856
}
17891857
}
1858+
if (set_head(remote_refs))
1859+
;
1860+
/*
1861+
* Way too many cases where this can go wrong
1862+
* so let's just fail silently for now.
1863+
*/
17901864

17911865
cleanup:
17921866
if (retcode) {

builtin/remote.c

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,11 +1399,40 @@ static int show(int argc, const char **argv, const char *prefix)
13991399
return result;
14001400
}
14011401

1402+
static void report_set_head_auto(const char *remote, const char *head_name,
1403+
struct strbuf *b_local_head, int was_detached) {
1404+
struct strbuf buf_prefix = STRBUF_INIT;
1405+
const char *prev_head = NULL;
1406+
1407+
strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
1408+
skip_prefix(b_local_head->buf, buf_prefix.buf, &prev_head);
1409+
1410+
if (prev_head && !strcmp(prev_head, head_name))
1411+
printf(_("'%s/HEAD' is unchanged and points to '%s'\n"),
1412+
remote, head_name);
1413+
else if (prev_head)
1414+
printf(_("'%s/HEAD' has changed from '%s' and now points to '%s'\n"),
1415+
remote, prev_head, head_name);
1416+
else if (!b_local_head->len)
1417+
printf(_("'%s/HEAD' is now created and points to '%s'\n"),
1418+
remote, head_name);
1419+
else if (was_detached && b_local_head->len)
1420+
printf(_("'%s/HEAD' was detached at '%s' and now points to '%s'\n"),
1421+
remote, b_local_head->buf, head_name);
1422+
else
1423+
printf(_("'%s/HEAD' used to point to '%s' "
1424+
"(which is not a remote branch), but now points to '%s'\n"),
1425+
remote, b_local_head->buf, head_name);
1426+
strbuf_release(&buf_prefix);
1427+
}
1428+
14021429
static int set_head(int argc, const char **argv, const char *prefix)
14031430
{
1404-
int i, opt_a = 0, opt_d = 0, result = 0;
1405-
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
1431+
int i, opt_a = 0, opt_d = 0, result = 0, was_detached;
1432+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
1433+
b_local_head = STRBUF_INIT;
14061434
char *head_name = NULL;
1435+
struct ref_store *refs = get_main_ref_store(the_repository);
14071436

14081437
struct option options[] = {
14091438
OPT_BOOL('a', "auto", &opt_a,
@@ -1415,7 +1444,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
14151444
argc = parse_options(argc, argv, prefix, options,
14161445
builtin_remote_sethead_usage, 0);
14171446
if (argc)
1418-
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
1447+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]);
14191448

14201449
if (!opt_a && !opt_d && argc == 2) {
14211450
head_name = xstrdup(argv[1]);
@@ -1434,25 +1463,32 @@ static int set_head(int argc, const char **argv, const char *prefix)
14341463
head_name = xstrdup(states.heads.items[0].string);
14351464
free_remote_ref_states(&states);
14361465
} else if (opt_d && !opt_a && argc == 1) {
1437-
if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
1438-
result |= error(_("Could not delete %s"), buf.buf);
1466+
if (refs_delete_ref(refs, NULL, b_head.buf, NULL, REF_NO_DEREF))
1467+
result |= error(_("Could not delete %s"), b_head.buf);
14391468
} else
14401469
usage_with_options(builtin_remote_sethead_usage, options);
14411470

1442-
if (head_name) {
1443-
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
1444-
/* make sure it's valid */
1445-
if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
1446-
result |= error(_("Not a valid ref: %s"), buf2.buf);
1447-
else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
1448-
result |= error(_("Could not setup %s"), buf.buf);
1449-
else if (opt_a)
1450-
printf("%s/HEAD set to %s\n", argv[0], head_name);
1451-
free(head_name);
1471+
if (!head_name)
1472+
goto cleanup;
1473+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", argv[0], head_name);
1474+
if (!refs_ref_exists(refs, b_remote_head.buf)) {
1475+
result |= error(_("Not a valid ref: %s"), b_remote_head.buf);
1476+
goto cleanup;
14521477
}
1453-
1454-
strbuf_release(&buf);
1455-
strbuf_release(&buf2);
1478+
was_detached = refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1479+
"remote set-head", &b_local_head, 0);
1480+
if (was_detached == -1) {
1481+
result |= error(_("Could not set up %s"), b_head.buf);
1482+
goto cleanup;
1483+
}
1484+
if (opt_a)
1485+
report_set_head_auto(argv[0], head_name, &b_local_head, was_detached);
1486+
1487+
cleanup:
1488+
free(head_name);
1489+
strbuf_release(&b_head);
1490+
strbuf_release(&b_remote_head);
1491+
strbuf_release(&b_local_head);
14561492
return result;
14571493
}
14581494

refs.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,20 +2115,54 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
21152115

21162116
int refs_update_symref(struct ref_store *refs, const char *ref,
21172117
const char *target, const char *logmsg)
2118+
{
2119+
return refs_update_symref_extended(refs, ref, target, logmsg, NULL, 0);
2120+
}
2121+
2122+
int refs_update_symref_extended(struct ref_store *refs, const char *ref,
2123+
const char *target, const char *logmsg,
2124+
struct strbuf *referent, int create_only)
21182125
{
21192126
struct ref_transaction *transaction;
21202127
struct strbuf err = STRBUF_INIT;
2121-
int ret = 0;
2128+
int ret = 0, prepret = 0;
21222129

21232130
transaction = ref_store_transaction_begin(refs, &err);
2124-
if (!transaction ||
2125-
ref_transaction_update(transaction, ref, NULL, NULL,
2126-
target, NULL, REF_NO_DEREF,
2127-
logmsg, &err) ||
2128-
ref_transaction_commit(transaction, &err)) {
2131+
if (!transaction) {
2132+
error_return:
21292133
ret = error("%s", err.buf);
2134+
goto cleanup;
21302135
}
2136+
if (create_only) {
2137+
if (ref_transaction_create(transaction, ref, NULL, target,
2138+
REF_NO_DEREF, logmsg, &err))
2139+
goto error_return;
2140+
prepret = ref_transaction_prepare(transaction, &err);
2141+
if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
2142+
goto error_return;
2143+
} else {
2144+
if (ref_transaction_update(transaction, ref, NULL, NULL,
2145+
target, NULL, REF_NO_DEREF,
2146+
logmsg, &err) ||
2147+
ref_transaction_prepare(transaction, &err))
2148+
goto error_return;
2149+
}
2150+
2151+
if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
2152+
struct object_id oid;
2153+
if (!refs_read_ref(refs, ref, &oid)) {
2154+
strbuf_addstr(referent, oid_to_hex(&oid));
2155+
ret = NOT_A_SYMREF;
2156+
}
2157+
}
2158+
2159+
if (prepret == TRANSACTION_CREATE_EXISTS)
2160+
goto cleanup;
2161+
2162+
if (ref_transaction_commit(transaction, &err))
2163+
goto error_return;
21312164

2165+
cleanup:
21322166
strbuf_release(&err);
21332167
if (transaction)
21342168
ref_transaction_free(transaction);
@@ -2951,4 +2985,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
29512985
return (update->flags & REF_HAVE_OLD) &&
29522986
(!is_null_oid(&update->old_oid) || update->old_target);
29532987
}
2954-

refs.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ int refs_read_ref_full(struct ref_store *refs, const char *refname,
8383

8484
int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid);
8585

86+
#define NOT_A_SYMREF -2
87+
88+
/*
89+
* Read the symbolic ref named "refname" and write its immediate referent into
90+
* the provided buffer. Referent is left empty if "refname" is not a symbolic
91+
* ref. It does not resolve the symbolic reference recursively in case the
92+
* target is also a symbolic ref.
93+
*
94+
* Returns 0 on success, -2 if the "refname" is not a symbolic ref,
95+
* -1 otherwise.
96+
*/
8697
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
8798
struct strbuf *referent);
8899

@@ -573,6 +584,10 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
573584
int refs_update_symref(struct ref_store *refs, const char *refname,
574585
const char *target, const char *logmsg);
575586

587+
int refs_update_symref_extended(struct ref_store *refs, const char *refname,
588+
const char *target, const char *logmsg,
589+
struct strbuf *referent, int create_only);
590+
576591
enum action_on_err {
577592
UPDATE_REFS_MSG_ON_ERR,
578593
UPDATE_REFS_DIE_ON_ERR,
@@ -758,8 +773,10 @@ int ref_transaction_verify(struct ref_transaction *transaction,
758773

759774
/* Naming conflict (for example, the ref names A and A/B conflict). */
760775
#define TRANSACTION_NAME_CONFLICT -1
776+
/* When only creation was requested, but the ref already exists. */
777+
#define TRANSACTION_CREATE_EXISTS -2
761778
/* All other errors. */
762-
#define TRANSACTION_GENERIC_ERROR -2
779+
#define TRANSACTION_GENERIC_ERROR -3
763780

764781
/*
765782
* Perform the preparatory stages of committing `transaction`. Acquire

refs/files-backend.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -596,10 +596,9 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
596596
unsigned int type;
597597

598598
ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1);
599-
if (ret)
600-
return ret;
601-
602-
return !(type & REF_ISSYMREF);
599+
if (!ret && !(type & REF_ISSYMREF))
600+
return NOT_A_SYMREF;
601+
return ret;
603602
}
604603

605604
int parse_loose_ref_contents(const struct git_hash_algo *algop,
@@ -2502,14 +2501,18 @@ static int split_symref_update(struct ref_update *update,
25022501
static int check_old_oid(struct ref_update *update, struct object_id *oid,
25032502
struct strbuf *err)
25042503
{
2504+
int ret = TRANSACTION_GENERIC_ERROR;
2505+
25052506
if (!(update->flags & REF_HAVE_OLD) ||
25062507
oideq(oid, &update->old_oid))
25072508
return 0;
25082509

2509-
if (is_null_oid(&update->old_oid))
2510+
if (is_null_oid(&update->old_oid)) {
25102511
strbuf_addf(err, "cannot lock ref '%s': "
25112512
"reference already exists",
25122513
ref_update_original_update_refname(update));
2514+
ret = TRANSACTION_CREATE_EXISTS;
2515+
}
25132516
else if (is_null_oid(oid))
25142517
strbuf_addf(err, "cannot lock ref '%s': "
25152518
"reference is missing but expected %s",
@@ -2522,7 +2525,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
25222525
oid_to_hex(oid),
25232526
oid_to_hex(&update->old_oid));
25242527

2525-
return -1;
2528+
return ret;
25262529
}
25272530

25282531
/*
@@ -2602,9 +2605,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26022605
ret = TRANSACTION_GENERIC_ERROR;
26032606
goto out;
26042607
}
2605-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2606-
ret = TRANSACTION_GENERIC_ERROR;
2607-
goto out;
2608+
} else {
2609+
ret = check_old_oid(update, &lock->old_oid, err);
2610+
if (ret) {
2611+
goto out;
2612+
}
26082613
}
26092614
} else {
26102615
/*
@@ -2635,9 +2640,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26352640
update->old_target);
26362641
ret = TRANSACTION_GENERIC_ERROR;
26372642
goto out;
2638-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2639-
ret = TRANSACTION_GENERIC_ERROR;
2640-
goto out;
2643+
} else {
2644+
ret = check_old_oid(update, &lock->old_oid, err);
2645+
if (ret) {
2646+
goto out;
2647+
}
26412648
}
26422649

26432650
/*

refs/refs-internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,11 @@ struct ref_storage_be {
673673

674674
ref_iterator_begin_fn *iterator_begin;
675675
read_raw_ref_fn *read_raw_ref;
676+
677+
/*
678+
* Please refer to `refs_read_symbolic_ref()` for the expected
679+
* behaviour.
680+
*/
676681
read_symbolic_ref_fn *read_symbolic_ref;
677682

678683
reflog_iterator_begin_fn *reflog_iterator_begin;

0 commit comments

Comments
 (0)