fetch set_head: fix non-mirror remotes in bare repositories

In b1b713f722 (fetch set_head: handle mirrored bare repositories, 2024-11-22) it was implicitly assumed that all remotes will be mirrors in a bare repository, thus fetching a non-mirrored remote could lead to HEAD pointing to a non-existent reference. Make sure we only overwrite HEAD if we are in a bare repository and fetching from a mirror. Otherwise, proceed as normally, and create refs/remotes/<nonmirrorremote>/HEAD instead. Reported-by: Christian Hesse <list@eworm.de> Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Bence Ferdinandy committed Jan 26, 2025 at 23:02 UTC 93dc16483a312b77c0b6533a6c2705d1df3e3687
3 files changed +32 -5
builtin/fetch.c
+5 -5
@@ -1619,7 +1619,7 @@ static void report_set_head(const char *remote, const char *head_name,
1619
1620 static int set_head(const struct ref *remote_refs, struct remote *remote)
1621 {
1622 - int result = 0, create_only, is_bare, was_detached;
1622 + int result = 0, create_only, baremirror, was_detached;
1623 struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
1624 b_local_head = STRBUF_INIT;
1625 int follow_remote_head = remote->follow_remote_head;
@@ -1655,9 +1655,9 @@ static int set_head(const struct ref *remote_refs, struct remote *remote)
1655
1656 if (!head_name)
1657 goto cleanup;
1658 - is_bare = is_bare_repository();
1659 - create_only = follow_remote_head == FOLLOW_REMOTE_ALWAYS ? 0 : !is_bare;
1660 - if (is_bare) {
1658 + baremirror = is_bare_repository() && remote->mirror;
1659 + create_only = follow_remote_head == FOLLOW_REMOTE_ALWAYS ? 0 : !baremirror;
1660 + if (baremirror) {
1661 strbuf_addstr(&b_head, "HEAD");
1662 strbuf_addf(&b_remote_head, "refs/heads/%s", head_name);
1663 } else {
@@ -1665,7 +1665,7 @@ static int set_head(const struct ref *remote_refs, struct remote *remote)
1665 strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote->name, head_name);
1666 }
1667 /* make sure it's valid */
1668 - if (!is_bare && !refs_ref_exists(refs, b_remote_head.buf)) {
1668 + if (!baremirror && !refs_ref_exists(refs, b_remote_head.buf)) {
1669 result = 1;
1670 goto cleanup;
1671 }
t/t5505-remote.sh
+10
@@ -589,6 +589,16 @@ test_expect_success 'add --mirror setting HEAD' '
589 )
590 '
591
592 +test_expect_success 'non-mirror fetch does not interfere with mirror' '
593 + test_when_finished rm -rf headnotmain &&
594 + (
595 + git init --bare -b notmain headnotmain &&
596 + cd headnotmain &&
597 + git remote add -f other ../two &&
598 + test "$(git symbolic-ref HEAD)" = "refs/heads/notmain"
599 + )
600 +'
601 +
602 test_expect_success 'add --mirror=fetch' '
603 mkdir mirror-fetch &&
604 git init -b main mirror-fetch/parent &&
t/t5510-fetch.sh
+17
@@ -84,6 +84,23 @@ test_expect_success "fetch test remote HEAD" '
84 branch=$(git rev-parse refs/remotes/origin/main) &&
85 test "z$head" = "z$branch"'
86
87 +test_expect_success "fetch test remote HEAD in bare repository" '
88 + test_when_finished rm -rf barerepo &&
89 + (
90 + cd "$D" &&
91 + git init --bare barerepo &&
92 + cd barerepo &&
93 + git remote add upstream ../two &&
94 + git fetch upstream &&
95 + git rev-parse --verify refs/remotes/upstream/HEAD &&
96 + git rev-parse --verify refs/remotes/upstream/main &&
97 + head=$(git rev-parse refs/remotes/upstream/HEAD) &&
98 + branch=$(git rev-parse refs/remotes/upstream/main) &&
99 + test "z$head" = "z$branch"
100 + )
101 +'
102 +
103 +
104 test_expect_success "fetch test remote HEAD change" '
105 cd "$D" &&
106 cd two &&