fetch: add branch.*.merge to default ref-prefix extension

When running "git pull" with no arguments, we'll do a default "git
fetch" and then try to merge the branch specified by the branch.*.merge
config. There's code in get_ref_map() to treat that "merge" branch as
something we want to fetch, even if it is not otherwise covered by the
default refspec.

This works fine with the v0 protocol, as the server tells us about all
of the refs, and get_ref_map() is the ultimate decider of what we fetch.

But in the v2 protocol, we send the ref-prefix extension to the server,
asking it to limit the ref advertisement. And we only tell it about the
default refspec for the remote; we don't mention the branch.*.merge
config at all.

This usually doesn't matter, because the default refspec matches
"refs/heads/*", which covers all branches. But if you explicitly use a
narrow refspec, then "git pull" on some branches may fail. The server
doesn't advertise the branch, so we don't fetch it, and "git pull"
thinks that it went away upstream.

We can fix this by including any branch.*.merge entries for the current
branch in the list of ref-prefixes we pass to the server. This only
needs to happen when using the default configured refspec (since
command-line refspecs are already added, and take precedence in deciding
what we fetch). We don't otherwise need to replicate any of the "what to
fetch" logic in get_ref_map(). These ref-prefixes are an optimization,
so it's OK if we tell the server to advertise the branch.*.merge ref,
even if we're not going to pull it. We'll just choose not to fetch it.

The test here is based on one constructed by Johannes. I modified the
branch names to trigger the ref-prefix issue (and be more descriptive),
and to confirm that "git pull" actually updated the local ref, which
should be more robust than just checking stderr.

Reported-by: Lana Deere <lana.deere@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jeff King 2022-09-08 15:26:09 -04:00 committed by Junio C Hamano
parent 080bc4990f
commit 49ca2fba39
2 changed files with 32 additions and 3 deletions

View File

@ -1626,9 +1626,21 @@ static int do_fetch(struct transport *transport,
break;
}
}
} else if (transport->remote->fetch.nr)
refspec_ref_prefixes(&transport->remote->fetch,
&transport_ls_refs_options.ref_prefixes);
} else {
struct branch *branch = branch_get(NULL);
if (transport->remote->fetch.nr)
refspec_ref_prefixes(&transport->remote->fetch,
&transport_ls_refs_options.ref_prefixes);
if (branch_has_merge_config(branch) &&
!strcmp(branch->remote_name, transport->remote->name)) {
int i;
for (i = 0; i < branch->merge_nr; i++) {
strvec_push(&transport_ls_refs_options.ref_prefixes,
branch->merge[i]->src);
}
}
}
if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
must_list_refs = 1;

View File

@ -218,6 +218,23 @@ test_expect_success 'fail if upstream branch does not exist' '
test_cmp expect file
'
test_expect_success 'fetch upstream branch even if refspec excludes it' '
# the branch names are not important here except that
# the first one must not be a prefix of the second,
# since otherwise the ref-prefix protocol extension
# would match both
git branch in-refspec HEAD^ &&
git branch not-in-refspec HEAD &&
git init -b in-refspec downstream &&
git -C downstream remote add -t in-refspec origin "file://$(pwd)/.git" &&
git -C downstream config branch.in-refspec.remote origin &&
git -C downstream config branch.in-refspec.merge refs/heads/not-in-refspec &&
git -C downstream pull &&
git rev-parse --verify not-in-refspec >expect &&
git -C downstream rev-parse --verify HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'fail if the index has unresolved entries' '
git checkout -b third second^ &&
test_when_finished "git checkout -f copy && git branch -D third" &&