2018-03-15 18:31:20 +01:00
|
|
|
#include "cache.h"
|
|
|
|
#include "repository.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "remote.h"
|
2020-07-28 22:23:39 +02:00
|
|
|
#include "strvec.h"
|
2018-03-15 18:31:20 +01:00
|
|
|
#include "ls-refs.h"
|
|
|
|
#include "pkt-line.h"
|
2018-12-18 13:47:50 +01:00
|
|
|
#include "config.h"
|
2018-03-15 18:31:20 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if one of the prefixes is a prefix of the ref.
|
|
|
|
* If no prefixes were provided, all refs match.
|
|
|
|
*/
|
2020-07-28 22:24:53 +02:00
|
|
|
static int ref_match(const struct strvec *prefixes, const char *refname)
|
2018-03-15 18:31:20 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2020-07-29 02:37:20 +02:00
|
|
|
if (!prefixes->nr)
|
2018-03-15 18:31:20 +01:00
|
|
|
return 1; /* no restriction */
|
|
|
|
|
2020-07-29 02:37:20 +02:00
|
|
|
for (i = 0; i < prefixes->nr; i++) {
|
|
|
|
const char *prefix = prefixes->v[i];
|
2018-03-15 18:31:20 +01:00
|
|
|
|
|
|
|
if (starts_with(refname, prefix))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ls_refs_data {
|
|
|
|
unsigned peel;
|
|
|
|
unsigned symrefs;
|
2020-07-28 22:24:53 +02:00
|
|
|
struct strvec prefixes;
|
2018-03-15 18:31:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static int send_ref(const char *refname, const struct object_id *oid,
|
|
|
|
int flag, void *cb_data)
|
|
|
|
{
|
|
|
|
struct ls_refs_data *data = cb_data;
|
|
|
|
const char *refname_nons = strip_namespace(refname);
|
|
|
|
struct strbuf refline = STRBUF_INIT;
|
|
|
|
|
2018-12-18 13:47:50 +01:00
|
|
|
if (ref_is_hidden(refname_nons, refname))
|
|
|
|
return 0;
|
|
|
|
|
2019-01-18 00:33:05 +01:00
|
|
|
if (!ref_match(&data->prefixes, refname_nons))
|
2018-03-15 18:31:20 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
|
|
|
|
if (data->symrefs && flag & REF_ISSYMREF) {
|
|
|
|
struct object_id unused;
|
|
|
|
const char *symref_target = resolve_ref_unsafe(refname, 0,
|
|
|
|
&unused,
|
|
|
|
&flag);
|
|
|
|
|
|
|
|
if (!symref_target)
|
|
|
|
die("'%s' is a symref but it is not?", refname);
|
|
|
|
|
upload-pack: strip namespace from symref data
Since 7171d8c15f (upload-pack: send symbolic ref information as
capability, 2013-09-17), we've sent cloning and fetching clients special
information about which branch HEAD is pointing to, so that they don't
have to guess based on matching up commit ids.
However, this feature has never worked properly with the GIT_NAMESPACE
feature. Because upload-pack uses head_ref_namespaced(find_symref), we
do find and report on refs/namespaces/foo/HEAD instead of the actual
HEAD of the repo. This makes sense, since the branch pointed to by the
top-level HEAD may not be advertised at all. But we do two things wrong:
1. We report the full name refs/namespaces/foo/HEAD, instead of just
HEAD. Meaning no client is going to bother doing anything with that
symref, since we're not otherwise advertising it.
2. We report the symref destination using its full name (e.g.,
refs/namespaces/foo/refs/heads/master). That's similarly useless to
the client, who only saw "refs/heads/master" in the advertisement.
We should be stripping the namespace prefix off of both places (which
this patch fixes).
Likely nobody noticed because we tend to do the right thing anyway. Bug
(1) means that we said nothing about HEAD (just refs/namespace/foo/HEAD).
And so the client half of the code, from a45b5f0552 (connect: annotate
refs with their symref information in get_remote_head(), 2013-09-17),
does not annotate HEAD, and we use the fallback in guess_remote_head(),
matching refs by object id. Which is usually right. It only falls down
in ambiguous cases, like the one laid out in the included test.
This also means that we don't have to worry about breaking anybody who
was putting pre-stripped names into their namespace symrefs when we fix
bug (2). Because of bug (1), nobody would have been using the symref we
advertised in the first place (not to mention that those symrefs would
have appeared broken for any non-namespaced access).
Note that we have separate fixes here for the v0 and v2 protocols. The
symref advertisement moved in v2 to be a part of the ls-refs command.
This actually gets part (1) right, since the symref annotation
piggy-backs on the existing ref advertisement, which is properly
stripped. But it still needs a fix for part (2). The included tests
cover both protocols.
Reported-by: Bryan Turner <bturner@atlassian.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-05-23 08:11:21 +02:00
|
|
|
strbuf_addf(&refline, " symref-target:%s",
|
|
|
|
strip_namespace(symref_target));
|
2018-03-15 18:31:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data->peel) {
|
|
|
|
struct object_id peeled;
|
|
|
|
if (!peel_ref(refname, &peeled))
|
|
|
|
strbuf_addf(&refline, " peeled:%s", oid_to_hex(&peeled));
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addch(&refline, '\n');
|
|
|
|
packet_write(1, refline.buf, refline.len);
|
|
|
|
|
|
|
|
strbuf_release(&refline);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-18 13:47:50 +01:00
|
|
|
static int ls_refs_config(const char *var, const char *value, void *data)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We only serve fetches over v2 for now, so respect only "uploadpack"
|
|
|
|
* config. This may need to eventually be expanded to "receive", but we
|
|
|
|
* don't yet know how that information will be passed to ls-refs.
|
|
|
|
*/
|
|
|
|
return parse_hide_refs_config(var, value, "uploadpack");
|
|
|
|
}
|
|
|
|
|
2020-07-28 22:24:53 +02:00
|
|
|
int ls_refs(struct repository *r, struct strvec *keys,
|
2018-03-15 18:31:20 +01:00
|
|
|
struct packet_reader *request)
|
|
|
|
{
|
|
|
|
struct ls_refs_data data;
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
2021-01-20 17:04:25 +01:00
|
|
|
strvec_init(&data.prefixes);
|
2018-03-15 18:31:20 +01:00
|
|
|
|
2018-12-18 13:47:50 +01:00
|
|
|
git_config(ls_refs_config, NULL);
|
|
|
|
|
2020-03-27 09:03:38 +01:00
|
|
|
while (packet_reader_read(request) == PACKET_READ_NORMAL) {
|
2018-03-15 18:31:20 +01:00
|
|
|
const char *arg = request->line;
|
|
|
|
const char *out;
|
|
|
|
|
|
|
|
if (!strcmp("peel", arg))
|
|
|
|
data.peel = 1;
|
|
|
|
else if (!strcmp("symrefs", arg))
|
|
|
|
data.symrefs = 1;
|
|
|
|
else if (skip_prefix(arg, "ref-prefix ", &out))
|
2020-07-28 22:24:53 +02:00
|
|
|
strvec_push(&data.prefixes, out);
|
2018-03-15 18:31:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-27 09:03:38 +01:00
|
|
|
if (request->status != PACKET_READ_FLUSH)
|
|
|
|
die(_("expected flush after ls-refs arguments"));
|
|
|
|
|
2018-03-15 18:31:20 +01:00
|
|
|
head_ref_namespaced(send_ref, &data);
|
ls-refs.c: traverse prefixes of disjoint "ref-prefix" sets
ls-refs performs a single revision walk over the whole ref namespace,
and sends ones that match with one of the given ref prefixes down to the
user.
This can be expensive if there are many refs overall, but the portion of
them covered by the given prefixes is small by comparison.
To attempt to reduce the difference between the number of refs
traversed, and the number of refs sent, only traverse references which
are in the longest common prefix of the given prefixes. This is very
reminiscent of the approach taken in b31e2680c4 (ref-filter.c: find
disjoint pattern prefixes, 2019-06-26) which does an analogous thing for
multi-patterned 'git for-each-ref' invocations.
The callback 'send_ref' is resilient to ignore extra patterns by
discarding any arguments which do not begin with at least one of the
specified prefixes.
Similarly, the code introduced in b31e2680c4 is resilient to stop early
at metacharacters, but we only pass strict prefixes here. At worst we
would return too many results, but the double checking done by send_ref
will throw away anything that doesn't start with something in the prefix
list.
Finally, if no prefixes were provided, then implicitly add the empty
string (which will match all references) since this matches the existing
behavior (see the "no restrictions" comment in "ls-refs.c:ref_match()").
Original-patch-by: Jacob Vosmaer <jacob@gitlab.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-20 17:04:30 +01:00
|
|
|
if (!data.prefixes.nr)
|
|
|
|
strvec_push(&data.prefixes, "");
|
|
|
|
for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
|
|
|
|
send_ref, &data, 0);
|
2018-03-15 18:31:20 +01:00
|
|
|
packet_flush(1);
|
2020-07-28 22:24:53 +02:00
|
|
|
strvec_clear(&data.prefixes);
|
2018-03-15 18:31:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|