Skip to content

Commit 498a669

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. Comments? * bf/set-head-symref: fetch: set remote/HEAD if it does not exist refs_update_symref: add create_only option transaction: add TRANSACTION_CREATE_EXISTS error set-head: better output for --auto set-head: add new variable for readability refs_update_symref: atomically record overwritten ref
2 parents 3e52bf7 + fa5ff60 commit 498a669

28 files changed

+481
-165
lines changed

builtin/branch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ static int replace_each_worktree_head_symref(struct worktree **worktrees,
559559
continue;
560560

561561
refs = get_worktree_ref_store(worktrees[i]);
562-
if (refs_update_symref(refs, "HEAD", newref, logmsg))
562+
if (refs_update_symref(refs, "HEAD", newref, logmsg, NULL, 0))
563563
ret = error(_("HEAD of working tree %s is not updated"),
564564
worktrees[i]->path);
565565
}

builtin/checkout.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
10151015
describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
10161016
}
10171017
} else if (new_branch_info->path) { /* Switch branches. */
1018-
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
1018+
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path,
1019+
msg.buf, NULL, 0) < 0)
10191020
die(_("unable to update HEAD"));
10201021
if (!opts->quiet) {
10211022
if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
@@ -1479,7 +1480,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
14791480
die(_("You are on a branch yet to be born"));
14801481
strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
14811482
status = refs_update_symref(get_main_ref_store(the_repository),
1482-
"HEAD", branch_ref.buf, "checkout -b");
1483+
"HEAD", branch_ref.buf, "checkout -b", NULL, 0);
14831484
strbuf_release(&branch_ref);
14841485
if (!opts->quiet)
14851486
fprintf(stderr, _("Switched to a new branch '%s'\n"),

builtin/clone.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ static void update_remote_refs(const struct ref *refs,
661661
strbuf_addstr(&head_ref, "HEAD");
662662
if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf,
663663
remote_head_points_at->peer_ref->name,
664-
msg) < 0)
664+
msg, NULL, 0) < 0)
665665
die(_("unable to update %s"), head_ref.buf);
666666
strbuf_release(&head_ref);
667667
}
@@ -673,7 +673,8 @@ static void update_head(const struct ref *our, const struct ref *remote,
673673
const char *head;
674674
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
675675
/* Local default branch link */
676-
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
676+
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name,
677+
NULL, NULL, 0) < 0)
677678
die(_("unable to update HEAD"));
678679
if (!option_bare) {
679680
refs_update_ref(get_main_ref_store(the_repository),
@@ -702,7 +703,8 @@ static void update_head(const struct ref *our, const struct ref *remote,
702703
* Unborn head from remote; same as "our" case above except
703704
* that we have no ref to update.
704705
*/
705-
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
706+
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn,
707+
NULL, NULL, 0) < 0)
706708
die(_("unable to update HEAD"));
707709
if (!option_bare)
708710
install_branch_config(0, head, remote_name, unborn);

builtin/fetch.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,80 @@ 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+
}
1596+
1597+
static const char *strip_refshead(const char *name){
1598+
skip_prefix(name, "refs/heads/", &name);
1599+
return name;
1600+
}
1601+
1602+
static int set_head(const struct ref *remote_refs)
1603+
{
1604+
int result = 0;
1605+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
1606+
b_local_head = STRBUF_INIT;
1607+
const char *remote = gtransport->remote->name;
1608+
char *head_name = NULL;
1609+
struct ref *ref, *matches;
1610+
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
1611+
struct refspec_item refspec = {
1612+
.force = 0,
1613+
.pattern = 1,
1614+
.src = (char *) "refs/heads/*",
1615+
.dst = (char *) "refs/heads/*",
1616+
};
1617+
struct string_list heads = STRING_LIST_INIT_DUP;
1618+
struct ref_store *refs = get_main_ref_store(the_repository);
1619+
1620+
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
1621+
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
1622+
fetch_map, 1);
1623+
for (ref = matches; ref; ref = ref->next) {
1624+
string_list_append(&heads, strip_refshead(ref->name));
1625+
}
1626+
1627+
1628+
if (!heads.nr)
1629+
result = 1;
1630+
else if (heads.nr > 1)
1631+
result = 1;
1632+
else
1633+
head_name = xstrdup(heads.items[0].string);
1634+
if (head_name) {
1635+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote);
1636+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name);
1637+
/* make sure it's valid */
1638+
if (!refs_ref_exists(refs, b_remote_head.buf))
1639+
result = 1;
1640+
else if (refs_update_symref(refs, b_head.buf, b_remote_head.buf,
1641+
"remote set-head", &b_local_head, 1))
1642+
result = 1;
1643+
else
1644+
report_set_head(remote, head_name, &b_local_head);
1645+
1646+
free(head_name);
1647+
}
1648+
1649+
strbuf_release(&b_head);
1650+
strbuf_release(&b_local_head);
1651+
strbuf_release(&b_remote_head);
1652+
return result;
1653+
}
1654+
15811655
static int do_fetch(struct transport *transport,
15821656
struct refspec *rs,
15831657
const struct fetch_config *config)
@@ -1647,6 +1721,8 @@ static int do_fetch(struct transport *transport,
16471721
"refs/tags/");
16481722
}
16491723

1724+
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1725+
16501726
if (must_list_refs) {
16511727
trace2_region_enter("fetch", "remote_refs", the_repository);
16521728
remote_refs = transport_get_remote_refs(transport,
@@ -1791,6 +1867,12 @@ static int do_fetch(struct transport *transport,
17911867
"you need to specify exactly one branch with the --set-upstream option"));
17921868
}
17931869
}
1870+
if (set_head(remote_refs))
1871+
;
1872+
/*
1873+
* Way too many cases where this can go wrong
1874+
* so let's just fail silently for now.
1875+
*/
17941876

17951877
cleanup:
17961878
if (retcode) {

builtin/notes.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,8 @@ static int merge(int argc, const char **argv, const char *prefix)
980980
die(_("a notes merge into %s is already in-progress at %s"),
981981
notes_ref, wt->path);
982982
free_worktrees(worktrees);
983-
if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", notes_ref, NULL))
983+
if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", notes_ref,
984+
NULL, NULL, 0))
984985
die(_("failed to store link to current notes ref (%s)"),
985986
notes_ref);
986987
fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "

builtin/remote.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ static int add(int argc, const char **argv, const char *prefix)
244244
strbuf_reset(&buf2);
245245
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
246246

247-
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
247+
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf,
248+
"remote add", NULL, 0))
248249
result = error(_("Could not setup master '%s'"), master);
249250
}
250251

@@ -864,7 +865,8 @@ static int mv(int argc, const char **argv, const char *prefix)
864865
strbuf_reset(&buf3);
865866
strbuf_addf(&buf3, "remote: renamed %s to %s",
866867
item->string, buf.buf);
867-
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
868+
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf,
869+
buf3.buf, NULL, 0))
868870
die(_("creating '%s' failed"), buf.buf);
869871
display_progress(progress, ++refs_renamed_nr);
870872
}
@@ -1399,11 +1401,36 @@ static int show(int argc, const char **argv, const char *prefix)
13991401
return result;
14001402
}
14011403

1404+
static void report_auto(const char *remote, const char *head_name,
1405+
struct strbuf *buf_prev) {
1406+
struct strbuf buf_prefix = STRBUF_INIT;
1407+
const char *prev_head = NULL;
1408+
1409+
strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
1410+
skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
1411+
1412+
if (prev_head && !strcmp(prev_head, head_name))
1413+
printf("'%s/HEAD' is unchanged and points to '%s'\n",
1414+
remote, head_name);
1415+
else if (prev_head)
1416+
printf("'%s/HEAD' has changed from '%s' and now points to '%s'\n",
1417+
remote, prev_head, head_name);
1418+
else if (buf_prev->len == 0)
1419+
printf("'%s/HEAD' is now created and points to '%s'\n",
1420+
remote, head_name);
1421+
else
1422+
printf("'%s/HEAD' used to point to '%s' "
1423+
"(which is unusual), but now points to '%s'\n",
1424+
remote, buf_prev->buf, head_name);
1425+
}
1426+
14021427
static int set_head(int argc, const char **argv, const char *prefix)
14031428
{
14041429
int i, opt_a = 0, opt_d = 0, result = 0;
1405-
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
1430+
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT,
1431+
buf_prev = STRBUF_INIT;
14061432
char *head_name = NULL;
1433+
struct ref_store *refs = get_main_ref_store(the_repository);
14071434

14081435
struct option options[] = {
14091436
OPT_BOOL('a', "auto", &opt_a,
@@ -1434,25 +1461,28 @@ static int set_head(int argc, const char **argv, const char *prefix)
14341461
head_name = xstrdup(states.heads.items[0].string);
14351462
free_remote_ref_states(&states);
14361463
} 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))
1464+
if (refs_delete_ref(refs, NULL, buf.buf, NULL, REF_NO_DEREF))
14381465
result |= error(_("Could not delete %s"), buf.buf);
14391466
} else
14401467
usage_with_options(builtin_remote_sethead_usage, options);
14411468

14421469
if (head_name) {
14431470
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
14441471
/* make sure it's valid */
1445-
if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
1472+
if (!refs_ref_exists(refs, buf2.buf))
14461473
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"))
1474+
else if (refs_update_symref(refs, buf.buf, buf2.buf,
1475+
"remote set-head", &buf_prev, 0))
14481476
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);
1477+
else if (opt_a) {
1478+
report_auto(argv[0], head_name, &buf_prev);
1479+
}
14511480
free(head_name);
14521481
}
14531482

14541483
strbuf_release(&buf);
14551484
strbuf_release(&buf2);
1485+
strbuf_release(&buf_prev);
14561486
return result;
14571487
}
14581488

builtin/symbolic-ref.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ int cmd_symbolic_ref(int argc,
8888
if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
8989
die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
9090
ret = !!refs_update_symref(get_main_ref_store(the_repository),
91-
argv[0], argv[1], msg);
91+
argv[0], argv[1], msg, NULL, 0);
9292
break;
9393
default:
9494
usage_with_options(git_symbolic_ref_usage, options);

builtin/worktree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ static int add_worktree(const char *path, const char *refname,
517517
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
518518
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
519519
else
520-
ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL);
520+
ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL, NULL, 0);
521521
if (ret)
522522
goto done;
523523

refs.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,25 +2114,44 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
21142114
}
21152115

21162116
int refs_update_symref(struct ref_store *refs, const char *ref,
2117-
const char *target, const char *logmsg)
2117+
const char *target, const char *logmsg,
2118+
struct strbuf *before_target, int create_only)
21182119
{
21192120
struct ref_transaction *transaction;
21202121
struct strbuf err = STRBUF_INIT;
21212122
int ret = 0;
21222123

21232124
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)) {
2125+
if (!transaction) {
2126+
error_return:
21292127
ret = error("%s", err.buf);
2128+
goto cleanup;
2129+
}
2130+
if (create_only) {
2131+
int create_ret = 0;
2132+
if (ref_transaction_create(transaction, ref, NULL, target,
2133+
REF_NO_DEREF, logmsg, &err))
2134+
goto error_return;
2135+
create_ret = ref_transaction_commit(transaction, &err);
2136+
if (create_ret && create_ret != TRANSACTION_CREATE_EXISTS)
2137+
ret = error("%s", err.buf);
2138+
2139+
} else {
2140+
if (ref_transaction_update(transaction, ref, NULL, NULL,
2141+
target, NULL, REF_NO_DEREF,
2142+
logmsg, &err) ||
2143+
ref_transaction_commit(transaction, &err))
2144+
goto error_return;
21302145
}
21312146

2147+
cleanup:
21322148
strbuf_release(&err);
2133-
if (transaction)
2134-
ref_transaction_free(transaction);
21352149

2150+
if (transaction) {
2151+
if (before_target && transaction->updates[0]->before_target)
2152+
strbuf_addstr(before_target, transaction->updates[0]->before_target);
2153+
ref_transaction_free(transaction);
2154+
}
21362155
return ret;
21372156
}
21382157

@@ -2948,4 +2967,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
29482967
return (update->flags & REF_HAVE_OLD) &&
29492968
(!is_null_oid(&update->old_oid) || update->old_target);
29502969
}
2951-

refs.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,8 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
571571
const char *newref, const char *logmsg);
572572

573573
int refs_update_symref(struct ref_store *refs, const char *refname,
574-
const char *target, const char *logmsg);
574+
const char *target, const char *logmsg,
575+
struct strbuf *before_target, int create_only);
575576

576577
enum action_on_err {
577578
UPDATE_REFS_MSG_ON_ERR,
@@ -758,8 +759,10 @@ int ref_transaction_verify(struct ref_transaction *transaction,
758759

759760
/* Naming conflict (for example, the ref names A and A/B conflict). */
760761
#define TRANSACTION_NAME_CONFLICT -1
762+
/* When only creation was requested, but the ref already exists. */
763+
#define TRANSACTION_CREATE_EXISTS -2
761764
/* All other errors. */
762-
#define TRANSACTION_GENERIC_ERROR -2
765+
#define TRANSACTION_GENERIC_ERROR -3
763766

764767
/*
765768
* Perform the preparatory stages of committing `transaction`. Acquire

0 commit comments

Comments
 (0)