for-each-ref: add --stdin option
When a user wishes to input a large list of patterns to 'git for-each-ref' (likely a long list of exact refs) there are frequently system limits on the number of command-line arguments. Add a new --stdin option to instead read the patterns from standard input. Add tests that check that any unrecognized arguments are considered an error when --stdin is provided. Also, an empty pattern list is interpreted as the complete ref set. When reading from stdin, we populate the filter.name_patterns array dynamically as opposed to pointing to the 'argv' array directly. This is simple when using a strvec, as it is NULL-terminated in the same way. We then free the memory directly from the strvec. Helped-by: Phillip Wood <phillip.wood123@gmail.com> Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
d15644fe02
commit
b73dec5530
@ -9,7 +9,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
|
||||
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
|
||||
[(--sort=<key>)...] [--format=<format>]
|
||||
[ --stdin | <pattern>... ]
|
||||
[--points-at=<object>]
|
||||
[--merged[=<object>]] [--no-merged[=<object>]]
|
||||
[--contains[=<object>]] [--no-contains[=<object>]]
|
||||
@ -32,6 +33,10 @@ OPTIONS
|
||||
literally, in the latter case matching completely or from the
|
||||
beginning up to a slash.
|
||||
|
||||
--stdin::
|
||||
If `--stdin` is supplied, then the list of patterns is read from
|
||||
standard input instead of from the argument list.
|
||||
|
||||
--count=<count>::
|
||||
By default the command shows all refs that match
|
||||
`<pattern>`. This option makes it stop after showing
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "object.h"
|
||||
#include "parse-options.h"
|
||||
#include "ref-filter.h"
|
||||
#include "strvec.h"
|
||||
|
||||
static char const * const for_each_ref_usage[] = {
|
||||
N_("git for-each-ref [<options>] [<pattern>]"),
|
||||
@ -25,6 +26,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||
struct ref_format format = REF_FORMAT_INIT;
|
||||
struct strbuf output = STRBUF_INIT;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
int from_stdin = 0;
|
||||
struct strvec vec = STRVEC_INIT;
|
||||
|
||||
struct option opts[] = {
|
||||
OPT_BIT('s', "shell", &format.quote_style,
|
||||
@ -49,6 +52,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
|
||||
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
|
||||
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||
OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@ -75,7 +79,23 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
|
||||
filter.ignore_case = icase;
|
||||
|
||||
filter.name_patterns = argv;
|
||||
if (from_stdin) {
|
||||
struct strbuf line = STRBUF_INIT;
|
||||
|
||||
if (argv[0])
|
||||
die(_("unknown arguments supplied with --stdin"));
|
||||
|
||||
while (strbuf_getline(&line, stdin) != EOF)
|
||||
strvec_push(&vec, line.buf);
|
||||
|
||||
strbuf_release(&line);
|
||||
|
||||
/* vec.v is NULL-terminated, just like 'argv'. */
|
||||
filter.name_patterns = vec.v;
|
||||
} else {
|
||||
filter.name_patterns = argv;
|
||||
}
|
||||
|
||||
filter.match_as_path = 1;
|
||||
filter_refs(&array, &filter, FILTER_REFS_ALL);
|
||||
ref_array_sort(sorting, &array);
|
||||
@ -97,5 +117,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||
free_commit_list(filter.with_commit);
|
||||
free_commit_list(filter.no_commit);
|
||||
ref_sorting_release(sorting);
|
||||
strvec_clear(&vec);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1464,4 +1464,41 @@ sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
|
||||
sig_crlf=${sig_crlf%dummy}
|
||||
test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
|
||||
|
||||
test_expect_success 'git for-each-ref --stdin: empty' '
|
||||
>in &&
|
||||
git for-each-ref --format="%(refname)" --stdin <in >actual &&
|
||||
git for-each-ref --format="%(refname)" >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git for-each-ref --stdin: fails if extra args' '
|
||||
>in &&
|
||||
test_must_fail git for-each-ref --format="%(refname)" \
|
||||
--stdin refs/heads/extra <in 2>err &&
|
||||
grep "unknown arguments supplied with --stdin" err
|
||||
'
|
||||
|
||||
test_expect_success 'git for-each-ref --stdin: matches' '
|
||||
cat >in <<-EOF &&
|
||||
refs/tags/multi*
|
||||
refs/heads/amb*
|
||||
EOF
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
refs/heads/ambiguous
|
||||
refs/tags/multi-ref1-100000-user1
|
||||
refs/tags/multi-ref1-100000-user2
|
||||
refs/tags/multi-ref1-200000-user1
|
||||
refs/tags/multi-ref1-200000-user2
|
||||
refs/tags/multi-ref2-100000-user1
|
||||
refs/tags/multi-ref2-100000-user2
|
||||
refs/tags/multi-ref2-200000-user1
|
||||
refs/tags/multi-ref2-200000-user2
|
||||
refs/tags/multiline
|
||||
EOF
|
||||
|
||||
git for-each-ref --format="%(refname)" --stdin <in >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user