[PATCH 2/2] builtin/show-ref.c: limit output with `--count`

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



`git show-ref` is a useful tool for answering existential questions
about whether or not certain (kinds of) references exist or not. For
example, to quickly determine whether or not a repository has any tags,
one could run:

    $ git show-ref -q --tags | head -1

and check whether there was any output.

But certain environments (like spawning Git processes from Ruby code) do
not have ergonomic ways to simulate sending SIGPIPE back to `show-ref`
when they have seen enough output.

Callers _could_ use `for-each-ref`, which has supports a `--count`
option to limit output, but this is sub-par for latency-sensitive
callers since `for-each-ref` buffers all results before printing
anything. In the above instance, even something like:

    $ git for-each-ref --count=1 -- 'refs/tags/*'

will enumerate every tag in a repository before deciding to print just
the first one. (The current behavior exists to accommodate sorting the
output from for-each-ref, and could be improved. See [1] for previous
work in this area).

In the meantime, introduce support for a similar `--count` option in
`show-ref`, so that callers can run:

    $ git show-ref -q --tags --count=1

to quickly determine whether a repository has any (e.g.) tags or not by
only enumerating at most one reference.

(This option is currently "incompatible" with `--verify`, though there
is no reason why the meaning of `--count` couldn't be extended to mean,
"with `--verify`, only verify `<n>` references".)

[1]: https://lore.kernel.org/git/YTNpQ7Od1U%2F5i0R7@xxxxxxxxxxxxxxxxxxxxxxx/

Signed-off-by: Taylor Blau <me@xxxxxxxxxxxx>
---
 Documentation/git-show-ref.txt |  7 ++++++-
 builtin/show-ref.c             | 23 ++++++++++++++++++++++-
 t/t1403-show-ref.sh            | 21 +++++++++++++++++++++
 3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index ab4d271925..28256c04dd 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git show-ref' [-q|--quiet] [--verify] [--head] [-d|--dereference]
 	     [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
-	     [--heads] [--] [<pattern>...]
+	     [--heads] [--count=<n>] [--] [<pattern>...]
 'git show-ref' --exclude-existing[=<pattern>]
 
 DESCRIPTION
@@ -84,6 +84,11 @@ OPTIONS
 	(4) ignore if refname is a ref that exists in the local repository;
 	(5) otherwise output the line.
 
+--count::
+
+	Do not print more than `<n>` matching references, or print all
+	references if `<n>` is 0. Incompatible with `--exclude-existing`
+	and `--verify`.
 
 <pattern>...::
 
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 17d5f98fe4..f0af8e8eec 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -15,7 +15,7 @@ static const char * const show_ref_usage[] = {
 };
 
 static int deref_tags, show_head, tags_only, heads_only, matches_nr, verify,
-	   quiet, hash_only, abbrev, exclude_arg;
+	   quiet, hash_only, abbrev, exclude_arg, max_count = 0;
 static const char **pattern;
 static const char *exclude_existing_arg;
 
@@ -82,6 +82,8 @@ static int show_ref(const char *refname, const struct object_id *oid,
 
 	show_one(refname, oid);
 
+	if (max_count && matches_nr >= max_count)
+		return -1; /* avoid opening any more refs */
 	return 0;
 }
 
@@ -178,6 +180,7 @@ static const struct option show_ref_options[] = {
 	OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg,
 		       N_("pattern"), N_("show refs from stdin that aren't in local repository"),
 		       PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
+	OPT_INTEGER(0, "count", &max_count, N_("show only <n> matched refs")),
 	OPT_END()
 };
 
@@ -188,6 +191,24 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, show_ref_options,
 			     show_ref_usage, 0);
 
+	if (max_count) {
+		int compatible = 0;
+
+		if (max_count < 0)
+			error(_("invalid --count argument: (`%d' < 0)"),
+			      max_count);
+		else if (verify)
+			error(_("--count is incompatible with %s"), "--verify");
+		else if (exclude_arg)
+			error(_("--count is incompatible with %s"),
+			      "--exclude-existing");
+		else
+			compatible = 1;
+
+		if (!compatible)
+			usage_with_options(show_ref_usage, show_ref_options);
+	}
+
 	if (exclude_arg)
 		return exclude_existing(exclude_existing_arg);
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 9252a581ab..b79e114c1e 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -196,4 +196,25 @@ test_expect_success 'show-ref --verify with dangling ref' '
 	)
 '
 
+test_expect_success 'show-ref --count limits relevant output' '
+	git show-ref --heads --count=1 >out &&
+	test_line_count = 1 out
+'
+
+test_expect_success 'show-ref --count rejects invalid input' '
+	test_must_fail git show-ref --count=-1 2>err &&
+	grep "invalid ..count argument: (.-1. < 0)" err
+'
+
+test_expect_success 'show-ref --count incompatible with --verify' '
+	test_must_fail git show-ref --count=1 --verify HEAD 2>err &&
+	grep "..count is incompatible with ..verify" err
+'
+
+test_expect_success 'show-ref --count incompatible with --exclude-existing' '
+	echo "refs/heads/main" >in &&
+	test_must_fail git show-ref --count=1 --exclude-existing <in 2>err &&
+	grep "..count is incompatible with ..exclude.existing" err
+'
+
 test_done
-- 
2.36.1.94.gb0d54bedca



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux