Skip to content

Commit ac92804

Browse files
ferdinandybttaylorr
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: Taylor Blau <[email protected]>
1 parent 066208a commit ac92804

12 files changed

+319
-124
lines changed

builtin/fetch.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,84 @@ 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+
strbuf_release(&buf_prefix);
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_extended(refs, b_head.buf, b_remote_head.buf,
1641+
"fetch", &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+
free_refs(fetch_map);
1650+
free_refs(matches);
1651+
string_list_clear(&heads, 0);
1652+
strbuf_release(&b_head);
1653+
strbuf_release(&b_local_head);
1654+
strbuf_release(&b_remote_head);
1655+
return result;
1656+
}
1657+
15801658
static int do_fetch(struct transport *transport,
15811659
struct refspec *rs,
15821660
const struct fetch_config *config)
@@ -1646,6 +1724,8 @@ static int do_fetch(struct transport *transport,
16461724
"refs/tags/");
16471725
}
16481726

1727+
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1728+
16491729
if (must_list_refs) {
16501730
trace2_region_enter("fetch", "remote_refs", the_repository);
16511731
remote_refs = transport_get_remote_refs(transport,
@@ -1790,6 +1870,12 @@ static int do_fetch(struct transport *transport,
17901870
"you need to specify exactly one branch with the --set-upstream option"));
17911871
}
17921872
}
1873+
if (set_head(remote_refs))
1874+
;
1875+
/*
1876+
* Way too many cases where this can go wrong
1877+
* so let's just fail silently for now.
1878+
*/
17931879

17941880
cleanup:
17951881
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: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ test_expect_success 'add another remote' '
7474
cd test &&
7575
git remote add -f second ../two &&
7676
tokens_match "origin second" "$(git remote)" &&
77-
check_tracking_branch second main side another &&
77+
check_tracking_branch second main side another HEAD &&
7878
git for-each-ref "--format=%(refname)" refs/remotes |
7979
sed -e "/^refs\/remotes\/origin\//d" \
8080
-e "/^refs\/remotes\/second\//d" >actual &&
@@ -476,7 +476,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
476476
cd test &&
477477
git fetch two "refs/heads/*:refs/remotes/two/*" &&
478478
git remote set-head --auto two >output 2>&1 &&
479-
echo "${SQ}two/HEAD${SQ} is now created and points to ${SQ}main${SQ}" >expect &&
479+
echo "${SQ}two/HEAD${SQ} is unchanged and points to ${SQ}main${SQ}" >expect &&
480480
test_cmp expect output
481481
)
482482
'
@@ -764,8 +764,10 @@ test_expect_success 'reject --no-no-tags' '
764764
'
765765

766766
cat >one/expect <<\EOF
767+
apis/HEAD -> apis/main
767768
apis/main
768769
apis/side
770+
drosophila/HEAD -> drosophila/main
769771
drosophila/another
770772
drosophila/main
771773
drosophila/side
@@ -783,19 +785,22 @@ test_expect_success 'update' '
783785
'
784786

785787
cat >one/expect <<\EOF
788+
drosophila/HEAD -> drosophila/main
786789
drosophila/another
787790
drosophila/main
788791
drosophila/side
792+
manduca/HEAD -> manduca/main
789793
manduca/main
790794
manduca/side
795+
megaloprepus/HEAD -> megaloprepus/main
791796
megaloprepus/main
792797
megaloprepus/side
793798
EOF
794799

795800
test_expect_success 'update with arguments' '
796801
(
797802
cd one &&
798-
for b in $(git branch -r)
803+
for b in $(git branch -r | grep -v HEAD)
799804
do
800805
git branch -r -d $b || exit 1
801806
done &&
@@ -827,18 +832,21 @@ test_expect_success 'update --prune' '
827832
'
828833

829834
cat >one/expect <<-\EOF
835+
apis/HEAD -> apis/main
830836
apis/main
831837
apis/side
838+
manduca/HEAD -> manduca/main
832839
manduca/main
833840
manduca/side
841+
megaloprepus/HEAD -> megaloprepus/main
834842
megaloprepus/main
835843
megaloprepus/side
836844
EOF
837845

838846
test_expect_success 'update default' '
839847
(
840848
cd one &&
841-
for b in $(git branch -r)
849+
for b in $(git branch -r | grep -v HEAD)
842850
do
843851
git branch -r -d $b || exit 1
844852
done &&
@@ -850,6 +858,7 @@ test_expect_success 'update default' '
850858
'
851859

852860
cat >one/expect <<\EOF
861+
drosophila/HEAD -> drosophila/main
853862
drosophila/another
854863
drosophila/main
855864
drosophila/side
@@ -858,7 +867,7 @@ EOF
858867
test_expect_success 'update default (overridden, with funny whitespace)' '
859868
(
860869
cd one &&
861-
for b in $(git branch -r)
870+
for b in $(git branch -r | grep -v HEAD)
862871
do
863872
git branch -r -d $b || exit 1
864873
done &&
@@ -872,7 +881,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
872881
test_expect_success 'update (with remotes.default defined)' '
873882
(
874883
cd one &&
875-
for b in $(git branch -r)
884+
for b in $(git branch -r | grep -v HEAD)
876885
do
877886
git branch -r -d $b || exit 1
878887
done &&

0 commit comments

Comments
 (0)