diff mbox series

[v3,4/6] revision: add new parameter to exclude hidden refs

Message ID de7c1aa210c2df9bdbbb6c19f44f72c37f56c5da.1667823042.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series receive-pack: only use visible refs for connectivity check | expand

Commit Message

Patrick Steinhardt Nov. 7, 2022, 12:16 p.m. UTC
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 option `--exclude-hidden=` that excludes any hidden refs from
the next pseudo-ref like `--all` or `--branches`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/rev-list-options.txt |   7 ++
 builtin/rev-list.c                 |   1 +
 revision.c                         |  43 +++++++-
 revision.h                         |   9 +-
 t/t6021-rev-list-exclude-hidden.sh | 159 +++++++++++++++++++++++++++++
 5 files changed, 217 insertions(+), 2 deletions(-)
 create mode 100755 t/t6021-rev-list-exclude-hidden.sh

Comments

Ævar Arnfjörð Bjarmason Nov. 7, 2022, 1:34 p.m. UTC | #1
On Mon, Nov 07 2022, Patrick Steinhardt wrote:

> +--exclude-hidden=[transfer|receive|uploadpack]::
> +	Do not include refs that have been hidden via either one of
> +	`transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` that

Maybe worth adding "(see linkgit:git-config[1]) after listing the config
variables.

>  int ref_excluded(const struct ref_exclusions *exclusions, const char *path)
>  {
> +	const char *stripped_path = strip_namespace(path);
>  	struct string_list_item *item;
> +

nit: stray whitespace in otherwise "clean" commit, but the post-image looks nicer, so...

>  void init_ref_exclusions(struct ref_exclusions *exclusions)
>  {
>  	string_list_init_dup(&exclusions->excluded_refs);
> +	string_list_init_dup(&exclusions->hidden_refs);
>  }

Per my comment on 3/6 we wouldn't need this when using the macro as a
source of truth.

>  void clear_ref_exclusions(struct ref_exclusions *exclusions)
>  {
>  	string_list_clear(&exclusions->excluded_refs, 0);
> +	string_list_clear(&exclusions->hidden_refs, 1);
>  }

Hrm, I'l read on, but I don't see any use of "util" here at a glance,
should the "1" here be "0", or maybe I've just missed how it's used...

> +	if (strcmp(section, "transfer") && strcmp(section, "receive") &&
> +	    strcmp(section, "uploadpack"))
> +		die(_("unsupported section for hidden refs: %s"), section);
> +
> +	if (exclusions->hidden_refs.nr)
> +		die(_("--exclude-hidden= passed more than once"));

We usually just ignore the first of --foo=bar --foo=baz and take "baz"
in our CLI use. Is it better to die here than just clear the previous
one & continue?


> -#define REF_EXCLUSIONS_INIT { .excluded_refs = STRING_LIST_INIT_DUP }
> +#define REF_EXCLUSIONS_INIT { .excluded_refs = STRING_LIST_INIT_DUP, .hidden_refs = STRING_LIST_INIT_DUP }

...the getting overly long line I mentioned in 3/6...

> +TEST_PASSES_SANITIZE_LEAK=true

Thanks for adding this! :)
Ævar Arnfjörð Bjarmason Nov. 7, 2022, 5:07 p.m. UTC | #2
On Mon, Nov 07 2022, Ævar Arnfjörð Bjarmason wrote:

> On Mon, Nov 07 2022, Patrick Steinhardt wrote:

>> +TEST_PASSES_SANITIZE_LEAK=true
>
> Thanks for adding this! :)

Hrm, I spoke too soon :) This series adds new leaks, so it'll fail with
the linux-leaks job. I have the following local monkeypatch on top,
which obviously doesn't address the root cause. The t6018 leak is new
due to the new tests you added.

diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index f92616de12d..54221588dd0 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,7 +5,6 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
index d08fc2da93d..908b9dba611 100755
--- a/t/t6021-rev-list-exclude-hidden.sh
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -2,7 +2,6 @@
 
 test_description='git rev-list --exclude-hidden test'
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
Taylor Blau Nov. 8, 2022, 12:57 a.m. UTC | #3
On Mon, Nov 07, 2022 at 01:16:35PM +0100, Patrick Steinhardt wrote:
> @@ -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=[transfer|receive|uploadpack]::
> +	Do not include refs that have been hidden via either one of
> +	`transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` that
> +	the next `--all`, `--branches`, `--tags`, `--remotes` or `--glob` would
> +	otherwise consider.  This option is cleared when seeing one of these
> +	pseudo-refs.
> +

Hmm. I thought that part of the motivation behind this round was to drop
the 'transfer' group, since it's implied by '--exclude-hidden=receive
--exclude-hidden-uploadpack', no?

Thanks,
Taylor
Patrick Steinhardt Nov. 8, 2022, 8:16 a.m. UTC | #4
On Mon, Nov 07, 2022 at 07:57:29PM -0500, Taylor Blau wrote:
> On Mon, Nov 07, 2022 at 01:16:35PM +0100, Patrick Steinhardt wrote:
> > @@ -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=[transfer|receive|uploadpack]::
> > +	Do not include refs that have been hidden via either one of
> > +	`transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` that
> > +	the next `--all`, `--branches`, `--tags`, `--remotes` or `--glob` would
> > +	otherwise consider.  This option is cleared when seeing one of these
> > +	pseudo-refs.
> > +
> 
> Hmm. I thought that part of the motivation behind this round was to drop
> the 'transfer' group, since it's implied by '--exclude-hidden=receive
> --exclude-hidden-uploadpack', no?
> 
> Thanks,
> Taylor

I didn't quite see the point in not providing the `transfer` group so
that users can ask for only the set of refs that are hidden by both
`uploadpack` and `receive`. But given that you're the second person
asking for it to be dropped now and given that I don't really have a
plausible usecase for this I'll drop it in the next version.

Patrick
Patrick Steinhardt Nov. 8, 2022, 9:22 a.m. UTC | #5
On Mon, Nov 07, 2022 at 02:34:45PM +0100, Ævar Arnfjörð Bjarmason wrote:
> On Mon, Nov 07 2022, Patrick Steinhardt wrote:
[snip]
> > +	if (strcmp(section, "transfer") && strcmp(section, "receive") &&
> > +	    strcmp(section, "uploadpack"))
> > +		die(_("unsupported section for hidden refs: %s"), section);
> > +
> > +	if (exclusions->hidden_refs.nr)
> > +		die(_("--exclude-hidden= passed more than once"));
> 
> We usually just ignore the first of --foo=bar --foo=baz and take "baz"
> in our CLI use. Is it better to die here than just clear the previous
> one & continue?

It's something I was torn on. I ultimately chose to die though because
of the difference between `--exclude` and `--exclude-hidden`: the former
one will happily add additional patterns, all of which will ultimately
be ignored. So as a user you might rightfully expect that the latter
will work the same: if both `--exclude-hidden=uploadpack` and
`--exclude-hidden=receive` are specified, you might want to have both be
ignored.

To me it wasn't quite clear how to support multiple instances of
`transfer.hideRefs` though as there is also the concept of un-hiding
already-hidden refs. So I wanted to avoid going into this discussion to
make the patch series a little bit smaller.

By dying instead of silently overriding the previous argument we retain
the ability to iterate on this at a later point though to implement
above behaviour, if the usecase ever arises.

Patrick
Patrick Steinhardt Nov. 8, 2022, 9:48 a.m. UTC | #6
On Mon, Nov 07, 2022 at 06:07:01PM +0100, Ævar Arnfjörð Bjarmason wrote:
> 
> On Mon, Nov 07 2022, Ævar Arnfjörð Bjarmason wrote:
> 
> > On Mon, Nov 07 2022, Patrick Steinhardt wrote:
> 
> >> +TEST_PASSES_SANITIZE_LEAK=true
> >
> > Thanks for adding this! :)
> 
> Hrm, I spoke too soon :) This series adds new leaks, so it'll fail with
> the linux-leaks job. I have the following local monkeypatch on top,
> which obviously doesn't address the root cause. The t6018 leak is new
> due to the new tests you added.

Right, I didn't know it was as easy to run tests with leak checking as
just executing `make test SANITIZE=leak`. Anyway, I did that now and the
issue is in fact in how the hidden refs are parsed because we already
`xstrdup()` the config value as we need to modify it anyway.

The following patch fixes the issue:

diff --git a/refs.c b/refs.c
index f1711e2e9f..2c7e88b190 100644
--- a/refs.c
+++ b/refs.c
@@ -1430,7 +1430,7 @@ 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';
-		string_list_append(hide_refs, ref);
+		string_list_append_nodup(hide_refs, ref);
 	}
 	return 0;
 }

Patrick
Jeff King Nov. 8, 2022, 2:42 p.m. UTC | #7
On Tue, Nov 08, 2022 at 09:16:47AM +0100, Patrick Steinhardt wrote:

> > > +--exclude-hidden=[transfer|receive|uploadpack]::
> > > +	Do not include refs that have been hidden via either one of
> > > +	`transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` that
> > > +	the next `--all`, `--branches`, `--tags`, `--remotes` or `--glob` would
> > > +	otherwise consider.  This option is cleared when seeing one of these
> > > +	pseudo-refs.
> > > +
> > 
> > Hmm. I thought that part of the motivation behind this round was to drop
> > the 'transfer' group, since it's implied by '--exclude-hidden=receive
> > --exclude-hidden-uploadpack', no?
> > 
> > Thanks,
> > Taylor
> 
> I didn't quite see the point in not providing the `transfer` group so
> that users can ask for only the set of refs that are hidden by both
> `uploadpack` and `receive`. But given that you're the second person
> asking for it to be dropped now and given that I don't really have a
> plausible usecase for this I'll drop it in the next version.

Sorry, I'm a little slow on the review. I just left a message in
response to v2 saying that I'm OK with it _if_ it's explained. But the
explanation above still seems misleading. Saying "either one of" implies
that they are mutually exclusive, but "receive" is really pulling from
"receive.hideRefs" and "transfer.hideRefs".

I think you'd need to lay out the rules. But if we just drop "transfer"
then that is simpler still (but the explanation probably still ought to
become "refs hidden by receive-pack" and not mention receive.hideRefs
explicitly).

-Peff
diff mbox series

Patch

diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 1837509566..a178956613 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -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=[transfer|receive|uploadpack]::
+	Do not include refs that have been hidden via either one of
+	`transfer.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` that
+	the next `--all`, `--branches`, `--tags`, `--remotes` or `--glob` would
+	otherwise consider.  This option is cleared when seeing one of these
+	pseudo-refs.
+
 --reflog::
 	Pretend as if all objects mentioned by reflogs are listed on the
 	command line as `<commit>`.
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 3acd93f71e..9eace06385 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"
+"    --exclude-hidden=[transfer|receive|uploadpack]\n"
 "    --quiet\n"
 "  ordering output:\n"
 "    --topo-order\n"
diff --git a/revision.c b/revision.c
index e5eaaa24ba..45652f9b0b 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"
@@ -1519,22 +1520,30 @@  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)
 {
 	string_list_init_dup(&exclusions->excluded_refs);
+	string_list_init_dup(&exclusions->hidden_refs);
 }
 
 void clear_ref_exclusions(struct ref_exclusions *exclusions)
 {
 	string_list_clear(&exclusions->excluded_refs, 0);
+	string_list_clear(&exclusions->hidden_refs, 1);
 }
 
 void add_ref_exclusion(struct ref_exclusions *exclusions, const char *exclude)
@@ -1542,6 +1551,35 @@  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;
+	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, "transfer") && strcmp(section, "receive") &&
+	    strcmp(section, "uploadpack"))
+		die(_("unsupported section for hidden refs: %s"), section);
+
+	if (exclusions->hidden_refs.nr)
+		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;
@@ -2220,7 +2258,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="))
 	{
@@ -2709,6 +2747,9 @@  static int handle_revision_pseudo_opt(struct rev_info *revs,
 	} 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;
 		init_all_refs_cb(&cb, revs, *flags);
diff --git a/revision.h b/revision.h
index 87d6824c55..fef5e063d1 100644
--- a/revision.h
+++ b/revision.h
@@ -87,6 +87,12 @@  struct ref_exclusions {
 	 * 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;
 };
 
 struct oidset;
@@ -449,12 +455,13 @@  void show_object_with_name(FILE *, struct object *, const char *);
 /**
  * Helpers to check if a reference should be excluded.
  */
-#define REF_EXCLUSIONS_INIT { .excluded_refs = STRING_LIST_INIT_DUP }
+#define REF_EXCLUSIONS_INIT { .excluded_refs = STRING_LIST_INIT_DUP, .hidden_refs = STRING_LIST_INIT_DUP }
 
 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
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
new file mode 100755
index 0000000000..d08fc2da93
--- /dev/null
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -0,0 +1,159 @@ 
+#!/bin/sh
+
+test_description='git rev-list --exclude-hidden 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_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
+'
+
+test_expect_success 'passed multiple times' '
+	echo "fatal: --exclude-hidden= passed more than once" >expected &&
+	test_must_fail git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=transfer --exclude-hidden=transfer 2>err &&
+	test_cmp expected err
+'
+
+test_expect_success '--exclude-hidden without hiddenRefs' '
+	git rev-list --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$NAMESPACE
+	$HIDDEN
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success 'hidden via transfer.hideRefs' '
+	git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$NAMESPACE
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--all --exclude-hidden=transfer --not --all without hidden refs' '
+	git rev-list --all --exclude-hidden=transfer --not --all >out &&
+	test_must_be_empty out
+'
+
+test_expect_success '--all --exclude-hidden=transfer --not --all with hidden ref' '
+	git -c transfer.hideRefs=refs/hidden/ rev-list --all --exclude-hidden=transfer --not --all >out &&
+	cat >expected <<-EOF &&
+	$HIDDEN
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--exclude-hidden with --exclude' '
+	git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$NAMESPACE
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--exclude-hidden is reset' '
+	git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=transfer --all --all >out &&
+	cat >expected <<-EOF &&
+	$NAMESPACE
+	$HIDDEN
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--exclude-hidden operates on stripped refs by default' '
+	GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaced/ rev-list --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$HIDDEN
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--exclude-hidden does not hide namespace by default' '
+	GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaces/namespace/ rev-list --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$NAMESPACE
+	$HIDDEN
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+test_expect_success '--exclude-hidden= may operate on unstripped refs' '
+	GIT_NAMESPACE=namespace git -c transfer.hideRefs=^refs/namespaces/namespace/ rev-list --exclude-hidden=transfer --all >out &&
+	cat >expected <<-EOF &&
+	$HIDDEN
+	$TAG
+	$COMMIT
+	EOF
+	test_cmp expected out
+'
+
+for section in receive uploadpack
+do
+	test_expect_success "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 "--exclude-hidden=$section respects 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 "--exclude-hidden=transfer ignores $section.hideRefs" '
+		git -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=transfer --all >out &&
+		cat >expected <<-EOF &&
+		$NAMESPACE
+		$HIDDEN
+		$TAG
+		$COMMIT
+		EOF
+		test_cmp expected out
+	'
+
+	test_expect_success "--exclude-hidden=$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
+	'
+done
+
+test_done