Users can optionally hide refs from remote users in git-upload-pack(1), git-receive-pack(1) and others via the `transfer.hideRefs`, but there is not an easy way to obtain the list of all visible or hidden refs right now. We'll require just that though for a performance improvement in our connectivity check. Add a new pseudo-ref `--visible-refs=` that pretends as if all refs have been added to the command line that are not hidden. The pseudo-ref requiers either one of "transfer", "uploadpack" or "receive" as argument to pay attention to `transfer.hideRefs`, `uploadpack.hideRefs` or `receive.hideRefs`, respectively. Signed-off-by: Patrick Steinhardt --- Documentation/rev-list-options.txt | 15 +++-- builtin/rev-list.c | 1 + builtin/rev-parse.c | 1 + revision.c | 34 +++++++++- t/t6021-rev-list-visible-refs.sh | 102 +++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 8 deletions(-) create mode 100755 t/t6021-rev-list-visible-refs.sh diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 1837509566..a0e34b0e2b 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -180,14 +180,19 @@ endif::git-log[] is automatically prepended if missing. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. +--visible-refs=[transfer|receive|uploadpack]:: + Pretend as if all the refs that have not been hidden via either one of + `transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` are + listed on the command line. + --exclude=:: Do not include refs matching '' that the next `--all`, - `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise - consider. Repetitions of this option accumulate exclusion patterns - up to the next `--all`, `--branches`, `--tags`, `--remotes`, or - `--glob` option (other options or arguments do not clear - accumulated patterns). + `--branches`, `--tags`, `--remotes`, `--glob` or `--visible-refs` would + otherwise consider. Repetitions of this option accumulate exclusion + patterns up to the next `--all`, `--branches`, `--tags`, `--remotes`, + `--glob` or `--visible-refs` option (other options or arguments do not + clear accumulated patterns). + The patterns given should not begin with `refs/heads`, `refs/tags`, or `refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`, diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 3acd93f71e..f719286cf8 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -38,6 +38,7 @@ static const char rev_list_usage[] = " --tags\n" " --remotes\n" " --stdin\n" +" --visible-refs=[transfer|receive|uploadpack]\n" " --quiet\n" " ordering output:\n" " --topo-order\n" diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 8f61050bde..31617bf3d5 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -77,6 +77,7 @@ static int is_rev_argument(const char *arg) "--topo-order", "--date-order", "--unpacked", + "--visible-refs=", NULL }; const char **p = rev_args; diff --git a/revision.c b/revision.c index 0760e78936..ef9e2947af 100644 --- a/revision.c +++ b/revision.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "object-store.h" #include "tag.h" #include "blob.h" @@ -1523,6 +1524,8 @@ struct all_refs_cb { struct rev_info *all_revs; const char *name_for_errormsg; struct worktree *wt; + struct string_list hidden_refs; + const char *hidden_refs_section; }; int ref_excluded(struct string_list *ref_excludes, const char *path) @@ -1542,11 +1545,13 @@ static int handle_one_ref(const char *path, const struct object_id *oid, int flag UNUSED, void *cb_data) { + const char *stripped_path = strip_namespace(path); struct all_refs_cb *cb = cb_data; struct object *object; - if (ref_excluded(cb->all_revs->ref_excludes, path)) - return 0; + if (ref_excluded(cb->all_revs->ref_excludes, path) || + ref_is_hidden(stripped_path, path, &cb->hidden_refs)) + return 0; object = get_reference(cb->all_revs, path, oid, cb->all_flags); add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags); @@ -1561,6 +1566,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs, cb->all_flags = flags; revs->rev_input_given = 1; cb->wt = NULL; + string_list_init_dup(&cb->hidden_refs); } void clear_ref_exclusion(struct string_list **ref_excludes_p) @@ -1596,6 +1602,13 @@ static void handle_refs(struct ref_store *refs, for_each(refs, handle_one_ref, &cb); } +static int hide_refs_config(const char *var, const char *value, void *cb_data) +{ + struct all_refs_cb *cb = cb_data; + return parse_hide_refs_config(var, value, cb->hidden_refs_section, + &cb->hidden_refs); +} + static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) { struct all_refs_cb *cb = cb_data; @@ -2225,7 +2238,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, "--visible-refs=") || starts_with(arg, "--branches=") || starts_with(arg, "--tags=") || starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk=")) { @@ -2759,6 +2772,21 @@ static int handle_revision_pseudo_opt(struct rev_info *revs, parse_list_objects_filter(&revs->filter, arg); } else if (!strcmp(arg, ("--no-filter"))) { list_objects_filter_set_no_filter(&revs->filter); + } else if (skip_prefix(arg, "--visible-refs=", &arg)) { + struct all_refs_cb cb; + + if (strcmp(arg, "transfer") && strcmp(arg, "receive") && + strcmp(arg, "uploadpack")) + die(_("unsupported section for --visible-refs: %s"), arg); + + init_all_refs_cb(&cb, revs, *flags); + cb.hidden_refs_section = arg; + git_config(hide_refs_config, &cb); + + refs_for_each_ref(refs, handle_one_ref, &cb); + + string_list_clear(&cb.hidden_refs, 1); + clear_ref_exclusion(&revs->ref_excludes); } else { return 0; } diff --git a/t/t6021-rev-list-visible-refs.sh b/t/t6021-rev-list-visible-refs.sh new file mode 100755 index 0000000000..9e12384dcf --- /dev/null +++ b/t/t6021-rev-list-visible-refs.sh @@ -0,0 +1,102 @@ +#!/bin/sh + +test_description='git rev-list --visible-refs test' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit_bulk --id=commit --ref=refs/heads/main 1 && + COMMIT=$(git rev-parse refs/heads/main) && + 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_expect_success 'invalid section' ' + echo "fatal: unsupported section for --visible-refs: unsupported" >expected && + test_must_fail git rev-list --visible-refs=unsupported 2>err && + test_cmp expected err +' + +test_expect_success '--visible-refs without hiddenRefs' ' + git rev-list --visible-refs=transfer >out && + cat >expected <<-EOF && + $HIDDEN + $TAG + $COMMIT + EOF + test_cmp expected out +' + +test_expect_success 'hidden via transfer.hideRefs' ' + git -c transfer.hideRefs=refs/hidden/ rev-list --visible-refs=transfer >out && + cat >expected <<-EOF && + $TAG + $COMMIT + EOF + test_cmp expected out +' + +test_expect_success '--all --not --visible-refs=transfer without hidden refs' ' + git rev-list --all --not --visible-refs=transfer >out && + test_must_be_empty out +' + +test_expect_success '--all --not --visible-refs=transfer with hidden ref' ' + git -c transfer.hideRefs=refs/hidden/ rev-list --all --not --visible-refs=transfer >out && + cat >expected <<-EOF && + $HIDDEN + EOF + test_cmp expected out +' + +test_expect_success '--visible-refs with --exclude' ' + git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --visible-refs=transfer >out && + cat >expected <<-EOF && + $COMMIT + EOF + test_cmp expected out +' + +for section in receive uploadpack +do + test_expect_success "hidden via $section.hideRefs" ' + git -c receive.hideRefs=refs/hidden/ rev-list --visible-refs=receive >out && + cat >expected <<-EOF && + $TAG + $COMMIT + EOF + test_cmp expected out + ' + + test_expect_success "--visible-refs=$section respects transfer.hideRefs" ' + git -c transfer.hideRefs=refs/hidden/ rev-list --visible-refs=$section >out && + cat >expected <<-EOF && + $TAG + $COMMIT + EOF + test_cmp expected out + ' + + test_expect_success "--visible-refs=transfer ignores $section.hideRefs" ' + git -c $section.hideRefs=refs/hidden/ rev-list --visible-refs=transfer >out && + cat >expected <<-EOF && + $HIDDEN + $TAG + $COMMIT + EOF + test_cmp expected out + ' + + test_expect_success "--visible-refs=$section respects both transfer.hideRefs and $section.hideRefs" ' + git -c transfer.hideRefs=refs/tags/ -c $section.hideRefs=refs/hidden/ rev-list --visible-refs=$section >out && + cat >expected <<-EOF && + $COMMIT + EOF + test_cmp expected out + ' +done + +test_done -- 2.38.1