`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