grep: add --max-count command line option
This patch adds a command line option analogous to that of GNU grep(1)'s -m / --max-count, which users might already be used to. This makes it possible to limit the amount of matches shown in the output while keeping the functionality of other options such as -C (show code context) or -p (show containing function), which would be difficult to do with a shell pipeline (e.g. head(1)). Signed-off-by: Carlos López 00xc@protonmail.com Signed-off-by: Junio C Hamano <gitster@pobox.com>
Carlos López committed
Jun 22, 2022 at 19:47 UTC
68437ede53dccd1dea9e44e831a59de274d389de
5 files changed
+108
-1
Documentation/git-grep.txt
+9
@@ -23,6 +23,7 @@ SYNOPSIS
23
[--break] [--heading] [-p | --show-function]
24
[-A <post-context>] [-B <pre-context>] [-C <context>]
25
[-W | --function-context]
26
+ [(-m | --max-count) <num>]
27
[--threads <num>]
28
[-f <file>] [-e] <pattern>
29
[--and|--or|--not|(|)|-e <pattern>...]
@@ -238,6 +239,14 @@ providing this option will cause it to die.
239
`git diff` works out patch hunk headers (see 'Defining a
240
custom hunk-header' in linkgit:gitattributes[5]).
241
242
+-m <num>::
243
+--max-count <num>::
244
+ Limit the amount of matches per file. When using the `-v` or
245
+ `--invert-match` option, the search stops after the specified
246
+ number of non-matches. A value of -1 will return unlimited
247
+ results (the default). A value of 0 will exit immediately with
248
+ a non-zero status.
249
+
250
--threads <num>::
251
Number of grep worker threads to use.
252
See `grep.threads` in 'CONFIGURATION' for more information.
builtin/grep.c
+9
@@ -961,6 +961,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
961
OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
962
N_("allow calling of grep(1) (ignored by this build)"),
963
PARSE_OPT_NOCOMPLETE),
964
+ OPT_INTEGER('m', "max-count", &opt.max_count,
965
+ N_("maximum number of results per file")),
966
OPT_END()
967
};
968
grep_prefix = prefix;
@@ -1101,6 +1103,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
1103
if (recurse_submodules && untracked)
1104
die(_("--untracked not supported with --recurse-submodules"));
1105
1106
+ /*
1107
+ * Optimize out the case where the amount of matches is limited to zero.
1108
+ * We do this to keep results consistent with GNU grep(1).
1109
+ */
1110
+ if (opt.max_count == 0)
1111
+ return 1;
1112
+
1113
if (show_in_pager) {
1114
if (num_threads > 1)
1115
warning(_("invalid option combination, ignoring --threads"));
grep.c
+1
-1
@@ -1615,7 +1615,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
1615
return 0;
1616
goto next_line;
1617
}
1618
- if (hit) {
1618
+ if (hit && (opt->max_count < 0 || count < opt->max_count)) {
1619
count++;
1620
if (opt->status_only)
1621
return 1;
grep.h
+2
@@ -171,6 +171,7 @@ struct grep_opt {
171
int show_hunk_mark;
172
int file_break;
173
int heading;
174
+ int max_count;
175
void *priv;
176
177
void (*output)(struct grep_opt *opt, const void *data, size_t size);
@@ -181,6 +182,7 @@ struct grep_opt {
182
.relative = 1, \
183
.pathname = 1, \
184
.max_depth = -1, \
185
+ .max_count = -1, \
186
.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED, \
187
.colors = { \
188
[GREP_COLOR_CONTEXT] = "", \
t/t7810-grep.sh
+87
@@ -77,6 +77,7 @@ test_expect_success setup '
77
# Say hello.
78
function hello() {
79
echo "Hello world."
80
+ echo "Hello again."
81
} # hello
82
83
# Still a no-op.
@@ -595,6 +596,92 @@ test_expect_success 'grep --files-without-match --quiet' '
596
test_must_be_empty actual
597
'
598
599
+test_expect_success 'grep --max-count 0 (must exit with non-zero)' '
600
+ test_must_fail git grep --max-count 0 foo >actual &&
601
+ test_must_be_empty actual
602
+'
603
+
604
+test_expect_success 'grep --max-count 3' '
605
+ cat >expected <<-EOF &&
606
+ file:foo mmap bar
607
+ file:foo_mmap bar
608
+ file:foo_mmap bar mmap
609
+ EOF
610
+ git grep --max-count 3 foo >actual &&
611
+ test_cmp expected actual
612
+'
613
+
614
+test_expect_success 'grep --max-count -1 (no limit)' '
615
+ cat >expected <<-EOF &&
616
+ file:foo mmap bar
617
+ file:foo_mmap bar
618
+ file:foo_mmap bar mmap
619
+ file:foo mmap bar_mmap
620
+ file:foo_mmap bar mmap baz
621
+ EOF
622
+ git grep --max-count -1 foo >actual &&
623
+ test_cmp expected actual
624
+'
625
+
626
+test_expect_success 'grep --max-count 1 --context 2' '
627
+ cat >expected <<-EOF &&
628
+ file-foo mmap bar
629
+ file:foo_mmap bar
630
+ file-foo_mmap bar mmap
631
+ EOF
632
+ git grep --max-count 1 --context 1 foo_mmap >actual &&
633
+ test_cmp expected actual
634
+'
635
+
636
+test_expect_success 'grep --max-count 1 --show-function' '
637
+ cat >expected <<-EOF &&
638
+ hello.ps1=function hello() {
639
+ hello.ps1: echo "Hello world."
640
+ EOF
641
+ git grep --max-count 1 --show-function Hello hello.ps1 >actual &&
642
+ test_cmp expected actual
643
+'
644
+
645
+test_expect_success 'grep --max-count 2 --show-function' '
646
+ cat >expected <<-EOF &&
647
+ hello.ps1=function hello() {
648
+ hello.ps1: echo "Hello world."
649
+ hello.ps1: echo "Hello again."
650
+ EOF
651
+ git grep --max-count 2 --show-function Hello hello.ps1 >actual &&
652
+ test_cmp expected actual
653
+'
654
+
655
+test_expect_success 'grep --max-count 1 --count' '
656
+ cat >expected <<-EOF &&
657
+ hello.ps1:1
658
+ EOF
659
+ git grep --max-count 1 --count Hello hello.ps1 >actual &&
660
+ test_cmp expected actual
661
+'
662
+
663
+test_expect_success 'grep --max-count 1 (multiple files)' '
664
+ cat >expected <<-EOF &&
665
+ hello.c:#include <stdio.h>
666
+ hello.ps1:# No-op.
667
+ EOF
668
+ git grep --max-count 1 -e o -- hello.\* >actual &&
669
+ test_cmp expected actual
670
+'
671
+
672
+test_expect_success 'grep --max-count 1 --context 1 (multiple files)' '
673
+ cat >expected <<-EOF &&
674
+ hello.c-#include <assert.h>
675
+ hello.c:#include <stdio.h>
676
+ hello.c-
677
+ --
678
+ hello.ps1:# No-op.
679
+ hello.ps1-function dummy() {}
680
+ EOF
681
+ git grep --max-count 1 --context 1 -e o -- hello.\* >actual &&
682
+ test_cmp expected actual
683
+'
684
+
685
cat >expected <<EOF
686
file:foo mmap bar_mmap
687
EOF