Merge branch 'ps/receive-use-only-advertised'

"git receive-pack" used to use all the local refs as the boundary for
checking connectivity of the data "git push" sent, but now it uses
only the refs that it advertised to the pusher. In a repository with
the .hideRefs configuration, this reduces the resources needed to
perform the check.
cf. <221028.86bkpw805n.gmgdl@evledraar.gmail.com>
cf. <xmqqr0yrizqm.fsf@gitster.g>

* ps/receive-use-only-advertised:
  receive-pack: only use visible refs for connectivity check
  rev-parse: add `--exclude-hidden=` option
  revision: add new parameter to exclude hidden refs
  revision: introduce struct to handle exclusions
  revision: move together exclusion-related functions
  refs: get rid of global list of hidden refs
  refs: fix memory leak when parsing hideRefs config
This commit is contained in:
Junio C Hamano 2022-11-23 11:22:25 +09:00
commit f8828f9125
15 changed files with 411 additions and 83 deletions

View File

@ -197,6 +197,13 @@ respectively, and they must begin with `refs/` when applied to `--glob`
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
explicitly.
--exclude-hidden=[receive|uploadpack]::
Do not include refs that would be hidden by `git-receive-pack` or
`git-upload-pack` by consulting the appropriate `receive.hideRefs` or
`uploadpack.hideRefs` configuration along with `transfer.hideRefs` (see
linkgit:git-config[1]). This option affects the next pseudo-ref option
`--all` or `--glob` and is cleared after processing them.
--disambiguate=<prefix>::
Show every object whose name begins with the given prefix.
The <prefix> must be at least 4 hexadecimal digits long to

View File

@ -195,6 +195,13 @@ respectively, and they must begin with `refs/` when applied to `--glob`
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
explicitly.
--exclude-hidden=[receive|uploadpack]::
Do not include refs that would be hidden by `git-receive-pack` or
`git-upload-pack` by consulting the appropriate `receive.hideRefs` or
`uploadpack.hideRefs` configuration along with `transfer.hideRefs` (see
linkgit:git-config[1]). This option affects the next pseudo-ref option
`--all` or `--glob` and is cleared after processing them.
--reflog::
Pretend as if all objects mentioned by reflogs are listed on the
command line as `<commit>`.

View File

@ -80,6 +80,7 @@ static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
static const char *cert_nonce_seed;
static struct string_list hidden_refs = STRING_LIST_INIT_DUP;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
static const char *NONCE_BAD = "BAD";
@ -130,7 +131,7 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
static int receive_pack_config(const char *var, const char *value, void *cb)
{
int status = parse_hide_refs_config(var, value, "receive");
int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
if (status)
return status;
@ -296,7 +297,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
struct oidset *seen = data;
const char *path = strip_namespace(path_full);
if (ref_is_hidden(path, path_full))
if (ref_is_hidden(path, path_full, &hidden_refs))
return 0;
/*
@ -1794,7 +1795,7 @@ static void reject_updates_to_hidden(struct command *commands)
strbuf_setlen(&refname_full, prefix_len);
strbuf_addstr(&refname_full, cmd->ref_name);
if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
if (!ref_is_hidden(cmd->ref_name, refname_full.buf, &hidden_refs))
continue;
if (is_null_oid(&cmd->new_oid))
cmd->error_string = "deny deleting a hidden ref";
@ -1928,6 +1929,8 @@ static void execute_commands(struct command *commands,
opt.err_fd = err_fd;
opt.progress = err_fd && !quiet;
opt.env = tmp_objdir_env(tmp_objdir);
opt.exclude_hidden_refs_section = "receive";
if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si);
@ -2591,6 +2594,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
packet_flush(1);
oid_array_clear(&shallow);
oid_array_clear(&ref);
string_list_clear(&hidden_refs, 0);
free((void *)push_cert_nonce);
return 0;
}

View File

@ -38,6 +38,7 @@ static const char rev_list_usage[] =
" --tags\n"
" --remotes\n"
" --stdin\n"
" --exclude-hidden=[receive|uploadpack]\n"
" --quiet\n"
" ordering output:\n"
" --topo-order\n"

View File

@ -39,7 +39,7 @@ static int abbrev_ref_strict;
static int output_sq;
static int stuck_long;
static struct string_list *ref_excludes;
static struct ref_exclusions ref_excludes = REF_EXCLUSIONS_INIT;
/*
* Some arguments are relevant "revision" arguments,
@ -198,7 +198,7 @@ static int show_default(void)
static int show_reference(const char *refname, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
{
if (ref_excluded(ref_excludes, refname))
if (ref_excluded(&ref_excludes, refname))
return 0;
show_rev(NORMAL, oid, refname);
return 0;
@ -585,7 +585,7 @@ static void handle_ref_opt(const char *pattern, const char *prefix)
for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
else
for_each_ref_in(prefix, show_reference, NULL);
clear_ref_exclusion(&ref_excludes);
clear_ref_exclusions(&ref_excludes);
}
enum format_type {
@ -863,7 +863,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--all")) {
for_each_ref(show_reference, NULL);
clear_ref_exclusion(&ref_excludes);
clear_ref_exclusions(&ref_excludes);
continue;
}
if (skip_prefix(arg, "--disambiguate=", &arg)) {
@ -876,10 +876,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
if (ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --branches"));
handle_ref_opt(arg, "refs/heads/");
continue;
}
if (opt_with_value(arg, "--tags", &arg)) {
if (ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --tags"));
handle_ref_opt(arg, "refs/tags/");
continue;
}
@ -888,6 +892,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (opt_with_value(arg, "--remotes", &arg)) {
if (ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --remotes"));
handle_ref_opt(arg, "refs/remotes/");
continue;
}
@ -895,6 +901,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
add_ref_exclusion(&ref_excludes, arg);
continue;
}
if (skip_prefix(arg, "--exclude-hidden=", &arg)) {
exclude_hidden_refs(&ref_excludes, arg);
continue;
}
if (!strcmp(arg, "--show-toplevel")) {
const char *work_tree = get_git_work_tree();
if (work_tree)

View File

@ -100,6 +100,9 @@ no_promisor_pack_found:
strvec_push(&rev_list.args, "--exclude-promisor-objects");
if (!opt->is_deepening_fetch) {
strvec_push(&rev_list.args, "--not");
if (opt->exclude_hidden_refs_section)
strvec_pushf(&rev_list.args, "--exclude-hidden=%s",
opt->exclude_hidden_refs_section);
strvec_push(&rev_list.args, "--all");
}
strvec_push(&rev_list.args, "--quiet");

View File

@ -46,6 +46,13 @@ struct check_connected_options {
* during a fetch.
*/
unsigned is_deepening_fetch : 1;
/*
* If not NULL, use `--exclude-hidden=$section` to exclude all refs
* hidden via the `$section.hideRefs` config from the set of
* already-reachable refs.
*/
const char *exclude_hidden_refs_section;
};
#define CHECK_CONNECTED_INIT { 0 }

View File

@ -6,6 +6,7 @@
#include "ls-refs.h"
#include "pkt-line.h"
#include "config.h"
#include "string-list.h"
static int config_read;
static int advertise_unborn;
@ -73,6 +74,7 @@ struct ls_refs_data {
unsigned symrefs;
struct strvec prefixes;
struct strbuf buf;
struct string_list hidden_refs;
unsigned unborn : 1;
};
@ -84,7 +86,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
strbuf_reset(&data->buf);
if (ref_is_hidden(refname_nons, refname))
if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
@ -137,14 +139,15 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
}
static int ls_refs_config(const char *var, const char *value,
void *data UNUSED)
void *cb_data)
{
struct ls_refs_data *data = cb_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");
return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
}
int ls_refs(struct repository *r, struct packet_reader *request)
@ -154,9 +157,10 @@ int ls_refs(struct repository *r, struct packet_reader *request)
memset(&data, 0, sizeof(data));
strvec_init(&data.prefixes);
strbuf_init(&data.buf, 0);
string_list_init_dup(&data.hidden_refs);
ensure_config_read();
git_config(ls_refs_config, NULL);
git_config(ls_refs_config, &data);
while (packet_reader_read(request) == PACKET_READ_NORMAL) {
const char *arg = request->line;
@ -195,6 +199,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
string_list_clear(&data.hidden_refs, 0);
return 0;
}

16
refs.c
View File

@ -1414,9 +1414,8 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
refname, strict);
}
static struct string_list *hide_refs;
int parse_hide_refs_config(const char *var, const char *value, const char *section)
int parse_hide_refs_config(const char *var, const char *value, const char *section,
struct string_list *hide_refs)
{
const char *key;
if (!strcmp("transfer.hiderefs", var) ||
@ -1431,21 +1430,16 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
len = strlen(ref);
while (len && ref[len - 1] == '/')
ref[--len] = '\0';
if (!hide_refs) {
CALLOC_ARRAY(hide_refs, 1);
hide_refs->strdup_strings = 1;
}
string_list_append(hide_refs, ref);
string_list_append_nodup(hide_refs, ref);
}
return 0;
}
int ref_is_hidden(const char *refname, const char *refname_full)
int ref_is_hidden(const char *refname, const char *refname_full,
const struct string_list *hide_refs)
{
int i;
if (!hide_refs)
return 0;
for (i = hide_refs->nr - 1; i >= 0; i--) {
const char *match = hide_refs->items[i].string;
const char *subject;

5
refs.h
View File

@ -808,7 +808,8 @@ int update_ref(const char *msg, const char *refname,
const struct object_id *new_oid, const struct object_id *old_oid,
unsigned int flags, enum action_on_err onerr);
int parse_hide_refs_config(const char *var, const char *value, const char *);
int parse_hide_refs_config(const char *var, const char *value, const char *,
struct string_list *);
/*
* Check whether a ref is hidden. If no namespace is set, both the first and
@ -818,7 +819,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);
* the ref is outside that namespace, the first parameter is NULL. The second
* parameter always points to the full ref name.
*/
int ref_is_hidden(const char *, const char *);
int ref_is_hidden(const char *, const char *, const struct string_list *);
/* Is this a per-worktree ref living in the refs/ namespace? */
int is_per_worktree_ref(const char *refname);

View File

@ -1,4 +1,5 @@
#include "cache.h"
#include "config.h"
#include "object-store.h"
#include "tag.h"
#include "blob.h"
@ -1517,6 +1518,69 @@ static void add_rev_cmdline_list(struct rev_info *revs,
}
}
int ref_excluded(const struct ref_exclusions *exclusions, const char *path)
{
const char *stripped_path = strip_namespace(path);
struct string_list_item *item;
for_each_string_list_item(item, &exclusions->excluded_refs) {
if (!wildmatch(item->string, path, 0))
return 1;
}
if (ref_is_hidden(stripped_path, path, &exclusions->hidden_refs))
return 1;
return 0;
}
void init_ref_exclusions(struct ref_exclusions *exclusions)
{
struct ref_exclusions blank = REF_EXCLUSIONS_INIT;
memcpy(exclusions, &blank, sizeof(*exclusions));
}
void clear_ref_exclusions(struct ref_exclusions *exclusions)
{
string_list_clear(&exclusions->excluded_refs, 0);
string_list_clear(&exclusions->hidden_refs, 0);
exclusions->hidden_refs_configured = 0;
}
void add_ref_exclusion(struct ref_exclusions *exclusions, const char *exclude)
{
string_list_append(&exclusions->excluded_refs, exclude);
}
struct exclude_hidden_refs_cb {
struct ref_exclusions *exclusions;
const char *section;
};
static int hide_refs_config(const char *var, const char *value, void *cb_data)
{
struct exclude_hidden_refs_cb *cb = cb_data;
cb->exclusions->hidden_refs_configured = 1;
return parse_hide_refs_config(var, value, cb->section,
&cb->exclusions->hidden_refs);
}
void exclude_hidden_refs(struct ref_exclusions *exclusions, const char *section)
{
struct exclude_hidden_refs_cb cb;
if (strcmp(section, "receive") && strcmp(section, "uploadpack"))
die(_("unsupported section for hidden refs: %s"), section);
if (exclusions->hidden_refs_configured)
die(_("--exclude-hidden= passed more than once"));
cb.exclusions = exclusions;
cb.section = section;
git_config(hide_refs_config, &cb);
}
struct all_refs_cb {
int all_flags;
int warned_bad_reflog;
@ -1525,19 +1589,6 @@ struct all_refs_cb {
struct worktree *wt;
};
int ref_excluded(struct string_list *ref_excludes, const char *path)
{
struct string_list_item *item;
if (!ref_excludes)
return 0;
for_each_string_list_item(item, ref_excludes) {
if (!wildmatch(item->string, path, 0))
return 1;
}
return 0;
}
static int handle_one_ref(const char *path, const struct object_id *oid,
int flag UNUSED,
void *cb_data)
@ -1545,7 +1596,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
struct all_refs_cb *cb = cb_data;
struct object *object;
if (ref_excluded(cb->all_revs->ref_excludes, path))
if (ref_excluded(&cb->all_revs->ref_excludes, path))
return 0;
object = get_reference(cb->all_revs, path, oid, cb->all_flags);
@ -1563,24 +1614,6 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
cb->wt = NULL;
}
void clear_ref_exclusion(struct string_list **ref_excludes_p)
{
if (*ref_excludes_p) {
string_list_clear(*ref_excludes_p, 0);
free(*ref_excludes_p);
}
*ref_excludes_p = NULL;
}
void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
{
if (!*ref_excludes_p) {
CALLOC_ARRAY(*ref_excludes_p, 1);
(*ref_excludes_p)->strdup_strings = 1;
}
string_list_append(*ref_excludes_p, exclude);
}
static void handle_refs(struct ref_store *refs,
struct rev_info *revs, unsigned flags,
int (*for_each)(struct ref_store *, each_ref_fn, void *))
@ -1886,6 +1919,7 @@ void repo_init_revisions(struct repository *r,
init_display_notes(&revs->notes_opt);
list_objects_filter_init(&revs->filter);
init_ref_exclusions(&revs->ref_excludes);
}
static void add_pending_commit_list(struct rev_info *revs,
@ -2210,7 +2244,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
!strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
!strcmp(arg, "--indexed-objects") ||
!strcmp(arg, "--alternate-refs") ||
starts_with(arg, "--exclude=") ||
starts_with(arg, "--exclude=") || starts_with(arg, "--exclude-hidden=") ||
starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
{
@ -2674,10 +2708,12 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
init_all_refs_cb(&cb, revs, *flags);
other_head_refs(handle_one_ref, &cb);
}
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) {
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --branches"));
handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) {
read_bisect_terms(&term_bad, &term_good);
handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
@ -2685,35 +2721,48 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
for_each_good_bisect_ref);
revs->bisect = 1;
} else if (!strcmp(arg, "--tags")) {
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --tags"));
handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--remotes")) {
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --remotes"));
handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref(handle_one_ref, optarg, &cb);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
return argcount;
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
add_ref_exclusion(&revs->ref_excludes, optarg);
return argcount;
} else if ((argcount = parse_long_opt("exclude-hidden", argv, &optarg))) {
exclude_hidden_refs(&revs->ref_excludes, optarg);
return argcount;
} else if (skip_prefix(arg, "--branches=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --branches"));
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --tags"));
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("--exclude-hidden cannot be used together with --remotes"));
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
clear_ref_exclusion(&revs->ref_excludes);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
add_reflogs_to_pending(revs, *flags);
} else if (!strcmp(arg, "--indexed-objects")) {

View File

@ -81,6 +81,35 @@ struct rev_cmdline_info {
} *rev;
};
struct ref_exclusions {
/*
* Excluded refs is a list of wildmatch patterns. If any of the
* patterns matches, the reference will be excluded.
*/
struct string_list excluded_refs;
/*
* Hidden refs is a list of patterns that is to be hidden via
* `ref_is_hidden()`.
*/
struct string_list hidden_refs;
/*
* Indicates whether hidden refs have been configured. This is to
* distinguish between no hidden refs existing and hidden refs not
* being parsed.
*/
char hidden_refs_configured;
};
/**
* Initialize a `struct ref_exclusions` with a macro.
*/
#define REF_EXCLUSIONS_INIT { \
.excluded_refs = STRING_LIST_INIT_DUP, \
.hidden_refs = STRING_LIST_INIT_DUP, \
}
struct oidset;
struct topo_walk_info;
@ -103,7 +132,7 @@ struct rev_info {
struct list_objects_filter_options filter;
/* excluding from --branches, --refs, etc. expansion */
struct string_list *ref_excludes;
struct ref_exclusions ref_excludes;
/* Basic information */
const char *prefix;
@ -455,12 +484,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)
void show_object_with_name(FILE *, struct object *, const char *);
/**
* Helpers to check if a "struct string_list" item matches with
* wildmatch().
* Helpers to check if a reference should be excluded.
*/
int ref_excluded(struct string_list *, const char *path);
void clear_ref_exclusion(struct string_list **);
void add_ref_exclusion(struct string_list **, const char *exclude);
int ref_excluded(const struct ref_exclusions *exclusions, const char *path);
void init_ref_exclusions(struct ref_exclusions *);
void clear_ref_exclusions(struct ref_exclusions *);
void add_ref_exclusion(struct ref_exclusions *, const char *exclude);
void exclude_hidden_refs(struct ref_exclusions *, const char *section);
/**
* This function can be used if you want to add commit objects as revision

View File

@ -187,6 +187,46 @@ test_expect_success 'rev-parse --exclude=ref with --remotes=glob' '
compare rev-parse "--exclude=upstream/x --remotes=upstream/*" "upstream/one upstream/two"
'
for section in receive uploadpack
do
test_expect_success "rev-parse --exclude-hidden=$section with --all" '
compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags" "--exclude-hidden=$section --all"
'
test_expect_success "rev-parse --exclude-hidden=$section with --all" '
compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --all" "--exclude-hidden=$section --all"
'
test_expect_success "rev-parse --exclude-hidden=$section with --glob" '
compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --glob=refs/heads/*" "--exclude-hidden=$section --glob=refs/heads/*"
'
test_expect_success "rev-parse --exclude-hidden=$section can be passed once per pseudo-ref" '
compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags --branches --tags" "--exclude-hidden=$section --all --exclude-hidden=$section --all"
'
test_expect_success "rev-parse --exclude-hidden=$section can only be passed once per pseudo-ref" '
echo "fatal: --exclude-hidden= passed more than once" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --exclude-hidden=$section 2>err &&
test_cmp expected err
'
for pseudoopt in branches tags remotes
do
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
test_cmp expected err
'
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
test_cmp expected err
'
done
done
test_expect_success 'rev-list --exclude=glob with --branches=glob' '
compare rev-list "--exclude=subspace-* --branches=sub*" "subspace/one subspace/two"
'

View File

@ -0,0 +1,163 @@
#!/bin/sh
test_description='git rev-list --exclude-hidden test'
. ./test-lib.sh
test_expect_success 'setup' '
test_commit_bulk --id=commit --ref=refs/heads/branch 1 &&
COMMIT=$(git rev-parse refs/heads/branch) &&
test_commit_bulk --id=tag --ref=refs/tags/lightweight 1 &&
TAG=$(git rev-parse refs/tags/lightweight) &&
test_commit_bulk --id=hidden --ref=refs/hidden/commit 1 &&
HIDDEN=$(git rev-parse refs/hidden/commit) &&
test_commit_bulk --id=namespace --ref=refs/namespaces/namespace/refs/namespaced/commit 1 &&
NAMESPACE=$(git rev-parse refs/namespaces/namespace/refs/namespaced/commit)
'
test_expect_success 'invalid section' '
echo "fatal: unsupported section for hidden refs: unsupported" >expected &&
test_must_fail git rev-list --exclude-hidden=unsupported 2>err &&
test_cmp expected err
'
for section in receive uploadpack
do
test_expect_success "$section: passed multiple times" '
echo "fatal: --exclude-hidden= passed more than once" >expected &&
test_must_fail git rev-list --exclude-hidden=$section --exclude-hidden=$section 2>err &&
test_cmp expected err
'
test_expect_success "$section: without hiddenRefs" '
git rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$HIDDEN
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: hidden via transfer.hideRefs" '
git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: hidden via $section.hideRefs" '
git -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: respects both transfer.hideRefs and $section.hideRefs" '
git -c transfer.hideRefs=refs/tags/ -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: negation without hidden refs marks everything as uninteresting" '
git rev-list --all --exclude-hidden=$section --not --all >out &&
test_must_be_empty out
'
test_expect_success "$section: negation with hidden refs marks them as interesting" '
git -c transfer.hideRefs=refs/hidden/ rev-list --all --exclude-hidden=$section --not --all >out &&
cat >expected <<-EOF &&
$HIDDEN
EOF
test_cmp expected out
'
test_expect_success "$section: hidden refs and excludes work together" '
git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: excluded hidden refs get reset" '
git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$HIDDEN
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: excluded hidden refs can be used with multiple pseudo-refs" '
git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --exclude-hidden=$section --all >out &&
test_must_be_empty out
'
test_expect_success "$section: works with --glob" '
git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --glob=refs/h* >out &&
cat >expected <<-EOF &&
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: operates on stripped refs by default" '
GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaced/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$HIDDEN
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: does not hide namespace by default" '
GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$NAMESPACE
$HIDDEN
$TAG
$COMMIT
EOF
test_cmp expected out
'
test_expect_success "$section: can operate on unstripped refs" '
GIT_NAMESPACE=namespace git -c transfer.hideRefs=^refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
cat >expected <<-EOF &&
$HIDDEN
$TAG
$COMMIT
EOF
test_cmp expected out
'
for pseudoopt in remotes branches tags
do
test_expect_success "$section: fails with --$pseudoopt" '
test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
'
test_expect_success "$section: fails with --$pseudoopt=pattern" '
test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
'
done
done
test_done

View File

@ -62,6 +62,7 @@ struct upload_pack_data {
struct object_array have_obj;
struct oid_array haves; /* v2 only */
struct string_list wanted_refs; /* v2 only */
struct string_list hidden_refs;
struct object_array shallows;
struct string_list deepen_not;
@ -118,6 +119,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
{
struct string_list symref = STRING_LIST_INIT_DUP;
struct string_list wanted_refs = STRING_LIST_INIT_DUP;
struct string_list hidden_refs = STRING_LIST_INIT_DUP;
struct object_array want_obj = OBJECT_ARRAY_INIT;
struct object_array have_obj = OBJECT_ARRAY_INIT;
struct oid_array haves = OID_ARRAY_INIT;
@ -130,6 +132,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
memset(data, 0, sizeof(*data));
data->symref = symref;
data->wanted_refs = wanted_refs;
data->hidden_refs = hidden_refs;
data->want_obj = want_obj;
data->have_obj = have_obj;
data->haves = haves;
@ -151,6 +154,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
{
string_list_clear(&data->symref, 1);
string_list_clear(&data->wanted_refs, 1);
string_list_clear(&data->hidden_refs, 0);
object_array_clear(&data->want_obj);
object_array_clear(&data->have_obj);
oid_array_clear(&data->haves);
@ -842,8 +846,8 @@ static void deepen(struct upload_pack_data *data, int depth)
* Checking for reachable shallows requires that our refs be
* marked with OUR_REF.
*/
head_ref_namespaced(check_ref, NULL);
for_each_namespaced_ref(check_ref, NULL);
head_ref_namespaced(check_ref, data);
for_each_namespaced_ref(check_ref, data);
get_reachable_list(data, &reachable_shallows);
result = get_shallow_commits(&reachable_shallows,
@ -1158,11 +1162,11 @@ static void receive_needs(struct upload_pack_data *data,
/* return non-zero if the ref is hidden, otherwise 0 */
static int mark_our_ref(const char *refname, const char *refname_full,
const struct object_id *oid)
const struct object_id *oid, const struct string_list *hidden_refs)
{
struct object *o = lookup_unknown_object(the_repository, oid);
if (ref_is_hidden(refname, refname_full)) {
if (ref_is_hidden(refname, refname_full, hidden_refs)) {
o->flags |= HIDDEN_REF;
return 1;
}
@ -1171,11 +1175,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
}
static int check_ref(const char *refname_full, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
int flag UNUSED, void *cb_data)
{
const char *refname = strip_namespace(refname_full);
struct upload_pack_data *data = cb_data;
mark_our_ref(refname, refname_full, oid);
mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
return 0;
}
@ -1204,7 +1209,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
struct object_id peeled;
struct upload_pack_data *data = cb_data;
if (mark_our_ref(refname_nons, refname, oid))
if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
return 0;
if (capabilities) {
@ -1327,7 +1332,7 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
if (parse_object_filter_config(var, value, data) < 0)
return -1;
return parse_hide_refs_config(var, value, "uploadpack");
return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
}
static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
@ -1375,8 +1380,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
advertise_shallow_grafts(1);
packet_flush(1);
} else {
head_ref_namespaced(check_ref, NULL);
for_each_namespaced_ref(check_ref, NULL);
head_ref_namespaced(check_ref, &data);
for_each_namespaced_ref(check_ref, &data);
}
if (!advertise_refs) {
@ -1441,6 +1446,7 @@ static int parse_want(struct packet_writer *writer, const char *line,
static int parse_want_ref(struct packet_writer *writer, const char *line,
struct string_list *wanted_refs,
struct string_list *hidden_refs,
struct object_array *want_obj)
{
const char *refname_nons;
@ -1451,7 +1457,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
struct strbuf refname = STRBUF_INIT;
strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
if (ref_is_hidden(refname_nons, refname.buf) ||
if (ref_is_hidden(refname_nons, refname.buf, hidden_refs) ||
read_ref(refname.buf, &oid)) {
packet_writer_error(writer, "unknown ref %s", refname_nons);
die("unknown ref %s", refname_nons);
@ -1508,7 +1514,7 @@ static void process_args(struct packet_reader *request,
continue;
if (data->allow_ref_in_want &&
parse_want_ref(&data->writer, arg, &data->wanted_refs,
&data->want_obj))
&data->hidden_refs, &data->want_obj))
continue;
/* process have line */
if (parse_have(arg, &data->haves))