Skip to content

Commit e0f42ed

Browse files
committed
Merge branch 'bf/set-head-symref' into seen
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 t/t5505-remote: set default branch to main
2 parents 1b917ad + 5f611f8 commit e0f42ed

17 files changed

+490
-154
lines changed

builtin/fetch.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,92 @@ static int backfill_tags(struct display_state *display_state,
15781578
return retcode;
15791579
}
15801580

1581+
static void report_set_head(const char *remote, const char *head_name,
1582+
struct strbuf *buf_prev) {
1583+
struct strbuf buf_prefix = STRBUF_INIT;
1584+
const char *prev_head = NULL;
1585+
1586+
strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
1587+
skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
1588+
1589+
if (prev_head && strcmp(prev_head, head_name)) {
1590+
printf("'HEAD' at '%s' has changed from '%s' to '%s'\n",
1591+
remote, prev_head, head_name);
1592+
printf("Run 'git remote set-head %s %s' to follow the change.\n",
1593+
remote, head_name);
1594+
}
1595+
strbuf_release(&buf_prefix);
1596+
}
1597+
1598+
static const char *strip_refshead(const char *name){
1599+
skip_prefix(name, "refs/heads/", &name);
1600+
return name;
1601+
}
1602+
1603+
static int set_head(const struct ref *remote_refs)
1604+
{
1605+
int result = 0;
1606+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
1607+
b_local_head = STRBUF_INIT;
1608+
const char *remote = gtransport->remote->name;
1609+
char *head_name = NULL;
1610+
struct ref *ref, *matches;
1611+
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
1612+
struct refspec_item refspec = {
1613+
.force = 0,
1614+
.pattern = 1,
1615+
.src = (char *) "refs/heads/*",
1616+
.dst = (char *) "refs/heads/*",
1617+
};
1618+
struct string_list heads = STRING_LIST_INIT_DUP;
1619+
struct ref_store *refs = get_main_ref_store(the_repository);
1620+
1621+
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
1622+
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
1623+
fetch_map, 1);
1624+
for (ref = matches; ref; ref = ref->next) {
1625+
string_list_append(&heads, strip_refshead(ref->name));
1626+
}
1627+
1628+
1629+
if (!heads.nr)
1630+
result = 1;
1631+
else if (heads.nr > 1)
1632+
result = 1;
1633+
else
1634+
head_name = xstrdup(heads.items[0].string);
1635+
if (head_name) {
1636+
int is_bare = is_bare_repository();
1637+
if (is_bare) {
1638+
strbuf_addstr(&b_head, "HEAD");
1639+
strbuf_addf(&b_remote_head, "refs/heads/%s", head_name);
1640+
} else {
1641+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote);
1642+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name);
1643+
}
1644+
/* make sure it's valid */
1645+
if (!is_bare && !refs_ref_exists(refs, b_remote_head.buf)) {
1646+
result = 1;
1647+
}
1648+
else if (refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1649+
"fetch", &b_local_head, !is_bare)) {
1650+
result = 1;
1651+
}
1652+
else
1653+
report_set_head(remote, head_name, &b_local_head);
1654+
1655+
free(head_name);
1656+
}
1657+
1658+
free_refs(fetch_map);
1659+
free_refs(matches);
1660+
string_list_clear(&heads, 0);
1661+
strbuf_release(&b_head);
1662+
strbuf_release(&b_local_head);
1663+
strbuf_release(&b_remote_head);
1664+
return result;
1665+
}
1666+
15811667
static int do_fetch(struct transport *transport,
15821668
struct refspec *rs,
15831669
const struct fetch_config *config)
@@ -1647,6 +1733,8 @@ static int do_fetch(struct transport *transport,
16471733
"refs/tags/");
16481734
}
16491735

1736+
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1737+
16501738
if (must_list_refs) {
16511739
trace2_region_enter("fetch", "remote_refs", the_repository);
16521740
remote_refs = transport_get_remote_refs(transport,
@@ -1791,6 +1879,12 @@ static int do_fetch(struct transport *transport,
17911879
"you need to specify exactly one branch with the --set-upstream option"));
17921880
}
17931881
}
1882+
if (set_head(remote_refs))
1883+
;
1884+
/*
1885+
* Way too many cases where this can go wrong
1886+
* so let's just fail silently for now.
1887+
*/
17941888

17951889
cleanup:
17961890
if (retcode) {

builtin/remote.c

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,11 +1399,37 @@ 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) {
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
1420+
printf(_("'%s/HEAD' used to point to '%s' "
1421+
"(which is not a remote branch), but now points to '%s'\n"),
1422+
remote, b_local_head->buf, head_name);
1423+
strbuf_release(&buf_prefix);
1424+
}
1425+
14021426
static int set_head(int argc, const char **argv, const char *prefix)
14031427
{
14041428
int i, opt_a = 0, opt_d = 0, result = 0;
1405-
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
1429+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
1430+
b_local_head = STRBUF_INIT;
14061431
char *head_name = NULL;
1432+
struct ref_store *refs = get_main_ref_store(the_repository);
14071433

14081434
struct option options[] = {
14091435
OPT_BOOL('a', "auto", &opt_a,
@@ -1415,7 +1441,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
14151441
argc = parse_options(argc, argv, prefix, options,
14161442
builtin_remote_sethead_usage, 0);
14171443
if (argc)
1418-
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
1444+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]);
14191445

14201446
if (!opt_a && !opt_d && argc == 2) {
14211447
head_name = xstrdup(argv[1]);
@@ -1434,25 +1460,27 @@ static int set_head(int argc, const char **argv, const char *prefix)
14341460
head_name = xstrdup(states.heads.items[0].string);
14351461
free_remote_ref_states(&states);
14361462
} 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);
1463+
if (refs_delete_ref(refs, NULL, b_head.buf, NULL, REF_NO_DEREF))
1464+
result |= error(_("Could not delete %s"), b_head.buf);
14391465
} else
14401466
usage_with_options(builtin_remote_sethead_usage, options);
14411467

14421468
if (head_name) {
1443-
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
1469+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", argv[0], head_name);
14441470
/* 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);
1471+
if (!refs_ref_exists(refs, b_remote_head.buf))
1472+
result |= error(_("Not a valid ref: %s"), b_remote_head.buf);
1473+
else if (refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1474+
"remote set-head", &b_local_head, 0))
1475+
result |= error(_("Could not setup %s"), b_head.buf);
14491476
else if (opt_a)
1450-
printf("%s/HEAD set to %s\n", argv[0], head_name);
1477+
report_set_head_auto(argv[0], head_name, &b_local_head);
14511478
free(head_name);
14521479
}
14531480

1454-
strbuf_release(&buf);
1455-
strbuf_release(&buf2);
1481+
strbuf_release(&b_head);
1482+
strbuf_release(&b_remote_head);
1483+
strbuf_release(&b_local_head);
14561484
return result;
14571485
}
14581486

refs.c

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,20 +2116,48 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
21162116

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

21242131
transaction = ref_store_transaction_begin(refs, &err);
2125-
if (!transaction ||
2126-
ref_transaction_update(transaction, ref, NULL, NULL,
2127-
target, NULL, REF_NO_DEREF,
2128-
logmsg, &err) ||
2129-
ref_transaction_commit(transaction, &err)) {
2132+
if (!transaction) {
2133+
error_return:
21302134
ret = error("%s", err.buf);
2135+
goto cleanup;
2136+
}
2137+
if (create_only) {
2138+
if (ref_transaction_create(transaction, ref, NULL, target,
2139+
REF_NO_DEREF, logmsg, &err))
2140+
goto error_return;
2141+
prepret = ref_transaction_prepare(transaction, &err);
2142+
if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
2143+
goto error_return;
2144+
} else {
2145+
if (ref_transaction_update(transaction, ref, NULL, NULL,
2146+
target, NULL, REF_NO_DEREF,
2147+
logmsg, &err) ||
2148+
ref_transaction_prepare(transaction, &err))
2149+
goto error_return;
21312150
}
21322151

2152+
if (referent)
2153+
refs_read_symbolic_ref(refs, ref, referent);
2154+
2155+
if (prepret == TRANSACTION_CREATE_EXISTS)
2156+
goto cleanup;
2157+
if (ref_transaction_commit(transaction, &err))
2158+
goto error_return;
2159+
2160+
cleanup:
21332161
strbuf_release(&err);
21342162
if (transaction)
21352163
ref_transaction_free(transaction);
@@ -2949,4 +2977,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
29492977
return (update->flags & REF_HAVE_OLD) &&
29502978
(!is_null_oid(&update->old_oid) || update->old_target);
29512979
}
2952-

refs.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
574574
int refs_update_symref(struct ref_store *refs, const char *refname,
575575
const char *target, const char *logmsg);
576576

577+
int refs_update_symref_extended(struct ref_store *refs, const char *refname,
578+
const char *target, const char *logmsg,
579+
struct strbuf *referent, int create_only);
580+
577581
enum action_on_err {
578582
UPDATE_REFS_MSG_ON_ERR,
579583
UPDATE_REFS_DIE_ON_ERR,
@@ -759,8 +763,10 @@ int ref_transaction_verify(struct ref_transaction *transaction,
759763

760764
/* Naming conflict (for example, the ref names A and A/B conflict). */
761765
#define TRANSACTION_NAME_CONFLICT -1
766+
/* When only creation was requested, but the ref already exists. */
767+
#define TRANSACTION_CREATE_EXISTS -2
762768
/* All other errors. */
763-
#define TRANSACTION_GENERIC_ERROR -2
769+
#define TRANSACTION_GENERIC_ERROR -3
764770

765771
/*
766772
* Perform the preparatory stages of committing `transaction`. Acquire

refs/files-backend.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,14 +2508,18 @@ static int split_symref_update(struct ref_update *update,
25082508
static int check_old_oid(struct ref_update *update, struct object_id *oid,
25092509
struct strbuf *err)
25102510
{
2511+
int ret = TRANSACTION_GENERIC_ERROR;
2512+
25112513
if (!(update->flags & REF_HAVE_OLD) ||
25122514
oideq(oid, &update->old_oid))
25132515
return 0;
25142516

2515-
if (is_null_oid(&update->old_oid))
2517+
if (is_null_oid(&update->old_oid)) {
25162518
strbuf_addf(err, "cannot lock ref '%s': "
25172519
"reference already exists",
25182520
ref_update_original_update_refname(update));
2521+
ret = TRANSACTION_CREATE_EXISTS;
2522+
}
25192523
else if (is_null_oid(oid))
25202524
strbuf_addf(err, "cannot lock ref '%s': "
25212525
"reference is missing but expected %s",
@@ -2528,7 +2532,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
25282532
oid_to_hex(oid),
25292533
oid_to_hex(&update->old_oid));
25302534

2531-
return -1;
2535+
return ret;
25322536
}
25332537

25342538
/*
@@ -2608,9 +2612,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26082612
ret = TRANSACTION_GENERIC_ERROR;
26092613
goto out;
26102614
}
2611-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2612-
ret = TRANSACTION_GENERIC_ERROR;
2613-
goto out;
2615+
} else {
2616+
ret = check_old_oid(update, &lock->old_oid, err);
2617+
if (ret) {
2618+
goto out;
2619+
}
26142620
}
26152621
} else {
26162622
/*
@@ -2641,9 +2647,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26412647
update->old_target);
26422648
ret = TRANSACTION_GENERIC_ERROR;
26432649
goto out;
2644-
} else if (check_old_oid(update, &lock->old_oid, err)) {
2645-
ret = TRANSACTION_GENERIC_ERROR;
2646-
goto out;
2650+
} else {
2651+
ret = check_old_oid(update, &lock->old_oid, err);
2652+
if (ret) {
2653+
goto out;
2654+
}
26472655
}
26482656

26492657
/*

refs/reftable-backend.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,10 +1223,13 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
12231223
goto done;
12241224
}
12251225
} else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
1226-
if (is_null_oid(&u->old_oid))
1226+
ret = TRANSACTION_NAME_CONFLICT;
1227+
if (is_null_oid(&u->old_oid)) {
12271228
strbuf_addf(err, _("cannot lock ref '%s': "
12281229
"reference already exists"),
12291230
ref_update_original_update_refname(u));
1231+
ret = TRANSACTION_CREATE_EXISTS;
1232+
}
12301233
else if (is_null_oid(&current_oid))
12311234
strbuf_addf(err, _("cannot lock ref '%s': "
12321235
"reference is missing but expected %s"),
@@ -1238,7 +1241,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
12381241
ref_update_original_update_refname(u),
12391242
oid_to_hex(&current_oid),
12401243
oid_to_hex(&u->old_oid));
1241-
ret = -1;
12421244
goto done;
12431245
}
12441246

t/t4207-log-decoration-colors.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ ${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
5959
${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
6060
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
6161
${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
62-
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
62+
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit}, \
63+
${c_reset}${c_remoteBranch}other/HEAD${c_reset}${c_commit})${c_reset} A1
6364
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
6465
${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
6566
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\

0 commit comments

Comments
 (0)