Skip to content

Commit 5f21268

Browse files
committed
Merge branch 'bf/set-head-symref'
When "git fetch $remote" notices that refs/remotes/$remote/HEAD is missing and discovers what branch the other side points with its HEAD, refs/remotes/$remote/HEAD is updated to point to it. * 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 d882f38 + b1b713f commit 5f21268

18 files changed

+440
-58
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
@@ -1403,12 +1403,41 @@ static int show(int argc, const char **argv, const char *prefix,
14031403
return result;
14041404
}
14051405

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

14131442
struct option options[] = {
14141443
OPT_BOOL('a', "auto", &opt_a,
@@ -1420,7 +1449,7 @@ static int set_head(int argc, const char **argv, const char *prefix,
14201449
argc = parse_options(argc, argv, prefix, options,
14211450
builtin_remote_sethead_usage, 0);
14221451
if (argc)
1423-
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
1452+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]);
14241453

14251454
if (!opt_a && !opt_d && argc == 2) {
14261455
head_name = xstrdup(argv[1]);
@@ -1439,25 +1468,32 @@ static int set_head(int argc, const char **argv, const char *prefix,
14391468
head_name = xstrdup(states.heads.items[0].string);
14401469
free_remote_ref_states(&states);
14411470
} else if (opt_d && !opt_a && argc == 1) {
1442-
if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
1443-
result |= error(_("Could not delete %s"), buf.buf);
1471+
if (refs_delete_ref(refs, NULL, b_head.buf, NULL, REF_NO_DEREF))
1472+
result |= error(_("Could not delete %s"), b_head.buf);
14441473
} else
14451474
usage_with_options(builtin_remote_sethead_usage, options);
14461475

1447-
if (head_name) {
1448-
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
1449-
/* make sure it's valid */
1450-
if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
1451-
result |= error(_("Not a valid ref: %s"), buf2.buf);
1452-
else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
1453-
result |= error(_("Could not setup %s"), buf.buf);
1454-
else if (opt_a)
1455-
printf("%s/HEAD set to %s\n", argv[0], head_name);
1456-
free(head_name);
1476+
if (!head_name)
1477+
goto cleanup;
1478+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", argv[0], head_name);
1479+
if (!refs_ref_exists(refs, b_remote_head.buf)) {
1480+
result |= error(_("Not a valid ref: %s"), b_remote_head.buf);
1481+
goto cleanup;
14571482
}
1458-
1459-
strbuf_release(&buf);
1460-
strbuf_release(&buf2);
1483+
was_detached = refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1484+
"remote set-head", &b_local_head, 0);
1485+
if (was_detached == -1) {
1486+
result |= error(_("Could not set up %s"), b_head.buf);
1487+
goto cleanup;
1488+
}
1489+
if (opt_a)
1490+
report_set_head_auto(argv[0], head_name, &b_local_head, was_detached);
1491+
1492+
cleanup:
1493+
free(head_name);
1494+
strbuf_release(&b_head);
1495+
strbuf_release(&b_remote_head);
1496+
strbuf_release(&b_local_head);
14611497
return result;
14621498
}
14631499

refs.c

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

21672167
int refs_update_symref(struct ref_store *refs, const char *ref,
21682168
const char *target, const char *logmsg)
2169+
{
2170+
return refs_update_symref_extended(refs, ref, target, logmsg, NULL, 0);
2171+
}
2172+
2173+
int refs_update_symref_extended(struct ref_store *refs, const char *ref,
2174+
const char *target, const char *logmsg,
2175+
struct strbuf *referent, int create_only)
21692176
{
21702177
struct ref_transaction *transaction;
21712178
struct strbuf err = STRBUF_INIT;
2172-
int ret = 0;
2179+
int ret = 0, prepret = 0;
21732180

21742181
transaction = ref_store_transaction_begin(refs, 0, &err);
2175-
if (!transaction ||
2176-
ref_transaction_update(transaction, ref, NULL, NULL,
2177-
target, NULL, REF_NO_DEREF,
2178-
logmsg, &err) ||
2179-
ref_transaction_commit(transaction, &err)) {
2182+
if (!transaction) {
2183+
error_return:
21802184
ret = error("%s", err.buf);
2185+
goto cleanup;
2186+
}
2187+
if (create_only) {
2188+
if (ref_transaction_create(transaction, ref, NULL, target,
2189+
REF_NO_DEREF, logmsg, &err))
2190+
goto error_return;
2191+
prepret = ref_transaction_prepare(transaction, &err);
2192+
if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
2193+
goto error_return;
2194+
} else {
2195+
if (ref_transaction_update(transaction, ref, NULL, NULL,
2196+
target, NULL, REF_NO_DEREF,
2197+
logmsg, &err) ||
2198+
ref_transaction_prepare(transaction, &err))
2199+
goto error_return;
2200+
}
2201+
2202+
if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
2203+
struct object_id oid;
2204+
if (!refs_read_ref(refs, ref, &oid)) {
2205+
strbuf_addstr(referent, oid_to_hex(&oid));
2206+
ret = NOT_A_SYMREF;
2207+
}
21812208
}
21822209

2210+
if (prepret == TRANSACTION_CREATE_EXISTS)
2211+
goto cleanup;
2212+
2213+
if (ref_transaction_commit(transaction, &err))
2214+
goto error_return;
2215+
2216+
cleanup:
21832217
strbuf_release(&err);
21842218
if (transaction)
21852219
ref_transaction_free(transaction);
@@ -2993,4 +3027,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
29933027
return (update->flags & REF_HAVE_OLD) &&
29943028
(!is_null_oid(&update->old_oid) || update->old_target);
29953029
}
2996-

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

@@ -604,6 +615,10 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
604615
int refs_update_symref(struct ref_store *refs, const char *refname,
605616
const char *target, const char *logmsg);
606617

618+
int refs_update_symref_extended(struct ref_store *refs, const char *refname,
619+
const char *target, const char *logmsg,
620+
struct strbuf *referent, int create_only);
621+
607622
enum action_on_err {
608623
UPDATE_REFS_MSG_ON_ERR,
609624
UPDATE_REFS_DIE_ON_ERR,
@@ -805,8 +820,10 @@ int ref_transaction_verify(struct ref_transaction *transaction,
805820

806821
/* Naming conflict (for example, the ref names A and A/B conflict). */
807822
#define TRANSACTION_NAME_CONFLICT -1
823+
/* When only creation was requested, but the ref already exists. */
824+
#define TRANSACTION_CREATE_EXISTS -2
808825
/* All other errors. */
809-
#define TRANSACTION_GENERIC_ERROR -2
826+
#define TRANSACTION_GENERIC_ERROR -3
810827

811828
/*
812829
* 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
@@ -598,10 +598,9 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
598598
unsigned int type;
599599

600600
ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1);
601-
if (ret)
602-
return ret;
603-
604-
return !(type & REF_ISSYMREF);
601+
if (!ret && !(type & REF_ISSYMREF))
602+
return NOT_A_SYMREF;
603+
return ret;
605604
}
606605

607606
int parse_loose_ref_contents(const struct git_hash_algo *algop,
@@ -2509,14 +2508,18 @@ static int split_symref_update(struct ref_update *update,
25092508
static int check_old_oid(struct ref_update *update, struct object_id *oid,
25102509
struct strbuf *err)
25112510
{
2511+
int ret = TRANSACTION_GENERIC_ERROR;
2512+
25122513
if (!(update->flags & REF_HAVE_OLD) ||
25132514
oideq(oid, &update->old_oid))
25142515
return 0;
25152516

2516-
if (is_null_oid(&update->old_oid))
2517+
if (is_null_oid(&update->old_oid)) {
25172518
strbuf_addf(err, "cannot lock ref '%s': "
25182519
"reference already exists",
25192520
ref_update_original_update_refname(update));
2521+
ret = TRANSACTION_CREATE_EXISTS;
2522+
}
25202523
else if (is_null_oid(oid))
25212524
strbuf_addf(err, "cannot lock ref '%s': "
25222525
"reference is missing but expected %s",
@@ -2529,7 +2532,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
25292532
oid_to_hex(oid),
25302533
oid_to_hex(&update->old_oid));
25312534

2532-
return -1;
2535+
return ret;
25332536
}
25342537

25352538
/*
@@ -2609,9 +2612,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26092612
ret = TRANSACTION_GENERIC_ERROR;
26102613
goto out;
26112614
}
2612-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2613-
ret = TRANSACTION_GENERIC_ERROR;
2614-
goto out;
2615+
} else {
2616+
ret = check_old_oid(update, &lock->old_oid, err);
2617+
if (ret) {
2618+
goto out;
2619+
}
26152620
}
26162621
} else {
26172622
/*
@@ -2642,9 +2647,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26422647
update->old_target);
26432648
ret = TRANSACTION_GENERIC_ERROR;
26442649
goto out;
2645-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2646-
ret = TRANSACTION_GENERIC_ERROR;
2647-
goto out;
2650+
} else {
2651+
ret = check_old_oid(update, &lock->old_oid, err);
2652+
if (ret) {
2653+
goto out;
2654+
}
26482655
}
26492656

26502657
/*

refs/refs-internal.h

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

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

679684
reflog_iterator_begin_fn *reflog_iterator_begin;

0 commit comments

Comments
 (0)