Skip to content

Commit fa5ff60

Browse files
ferdinandybgitster
authored andcommitted
fetch: set remote/HEAD if it does not exist
If the user has remote/HEAD set already and it looks like it has changed on the server, then print a message, otherwise set it if we can. Silently pass if the user already has the same remote/HEAD set as reported by the server or if we encounter any errors along the way. Signed-off-by: Bence Ferdinandy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c2a7244 commit fa5ff60

12 files changed

+308
-123
lines changed

builtin/fetch.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,80 @@ static int backfill_tags(struct display_state *display_state,
15771577
return retcode;
15781578
}
15791579

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

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

17941876
cleanup:
17951877
if (retcode) {

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}\

t/t5505-remote.sh

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ test_expect_success 'add another remote' '
7171
cd test &&
7272
git remote add -f second ../two &&
7373
tokens_match "origin second" "$(git remote)" &&
74-
check_tracking_branch second main side another &&
74+
check_tracking_branch second main side another HEAD &&
7575
git for-each-ref "--format=%(refname)" refs/remotes |
7676
sed -e "/^refs\/remotes\/origin\//d" \
7777
-e "/^refs\/remotes\/second\//d" >actual &&
@@ -473,7 +473,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
473473
cd test &&
474474
git fetch two "refs/heads/*:refs/remotes/two/*" &&
475475
git remote set-head --auto two >output 2>&1 &&
476-
echo "'\''two/HEAD'\'' is now created and points to '\''main'\''" >expect &&
476+
echo "'\''two/HEAD'\'' is unchanged and points to '\''main'\''" >expect &&
477477
test_cmp expect output
478478
)
479479
'
@@ -764,6 +764,7 @@ test_expect_success 'reject --no-no-tags' '
764764
cat >one/expect <<\EOF
765765
apis/main
766766
apis/side
767+
drosophila/HEAD -> drosophila/main
767768
drosophila/another
768769
drosophila/main
769770
drosophila/side
@@ -781,6 +782,7 @@ test_expect_success 'update' '
781782
'
782783

783784
cat >one/expect <<\EOF
785+
drosophila/HEAD -> drosophila/main
784786
drosophila/another
785787
drosophila/main
786788
drosophila/side
@@ -793,7 +795,7 @@ EOF
793795
test_expect_success 'update with arguments' '
794796
(
795797
cd one &&
796-
for b in $(git branch -r)
798+
for b in $(git branch -r | grep -v HEAD)
797799
do
798800
git branch -r -d $b || exit 1
799801
done &&
@@ -836,7 +838,7 @@ EOF
836838
test_expect_success 'update default' '
837839
(
838840
cd one &&
839-
for b in $(git branch -r)
841+
for b in $(git branch -r | grep -v HEAD)
840842
do
841843
git branch -r -d $b || exit 1
842844
done &&
@@ -848,6 +850,7 @@ test_expect_success 'update default' '
848850
'
849851

850852
cat >one/expect <<\EOF
853+
drosophila/HEAD -> drosophila/main
851854
drosophila/another
852855
drosophila/main
853856
drosophila/side
@@ -870,7 +873,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
870873
test_expect_success 'update (with remotes.default defined)' '
871874
(
872875
cd one &&
873-
for b in $(git branch -r)
876+
for b in $(git branch -r | grep -v HEAD)
874877
do
875878
git branch -r -d $b || exit 1
876879
done &&

0 commit comments

Comments
 (0)