Skip to content

Commit 8c4f65e

Browse files
committed
Merge branch 'cl/grep-max-count'
"git grep -m<max-hits>" is a way to limit the hits shown per file. * cl/grep-max-count: grep: add --max-count command line option
2 parents 884339a + 68437ed commit 8c4f65e

File tree

5 files changed

+108
-1
lines changed

5 files changed

+108
-1
lines changed

Documentation/git-grep.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SYNOPSIS
2323
[--break] [--heading] [-p | --show-function]
2424
[-A <post-context>] [-B <pre-context>] [-C <context>]
2525
[-W | --function-context]
26+
[(-m | --max-count) <num>]
2627
[--threads <num>]
2728
[-f <file>] [-e] <pattern>
2829
[--and|--or|--not|(|)|-e <pattern>...]
@@ -238,6 +239,14 @@ providing this option will cause it to die.
238239
`git diff` works out patch hunk headers (see 'Defining a
239240
custom hunk-header' in linkgit:gitattributes[5]).
240241

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+
241250
--threads <num>::
242251
Number of grep worker threads to use.
243252
See `grep.threads` in 'CONFIGURATION' for more information.

builtin/grep.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
961961
OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
962962
N_("allow calling of grep(1) (ignored by this build)"),
963963
PARSE_OPT_NOCOMPLETE),
964+
OPT_INTEGER('m', "max-count", &opt.max_count,
965+
N_("maximum number of results per file")),
964966
OPT_END()
965967
};
966968
grep_prefix = prefix;
@@ -1101,6 +1103,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11011103
if (recurse_submodules && untracked)
11021104
die(_("--untracked not supported with --recurse-submodules"));
11031105

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+
11041113
if (show_in_pager) {
11051114
if (num_threads > 1)
11061115
warning(_("invalid option combination, ignoring --threads"));

grep.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
16151615
return 0;
16161616
goto next_line;
16171617
}
1618-
if (hit) {
1618+
if (hit && (opt->max_count < 0 || count < opt->max_count)) {
16191619
count++;
16201620
if (opt->status_only)
16211621
return 1;

grep.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ struct grep_opt {
171171
int show_hunk_mark;
172172
int file_break;
173173
int heading;
174+
int max_count;
174175
void *priv;
175176

176177
void (*output)(struct grep_opt *opt, const void *data, size_t size);
@@ -181,6 +182,7 @@ struct grep_opt {
181182
.relative = 1, \
182183
.pathname = 1, \
183184
.max_depth = -1, \
185+
.max_count = -1, \
184186
.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED, \
185187
.colors = { \
186188
[GREP_COLOR_CONTEXT] = "", \

t/t7810-grep.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ test_expect_success setup '
7777
# Say hello.
7878
function hello() {
7979
echo "Hello world."
80+
echo "Hello again."
8081
} # hello
8182
8283
# Still a no-op.
@@ -595,6 +596,92 @@ test_expect_success 'grep --files-without-match --quiet' '
595596
test_must_be_empty actual
596597
'
597598

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+
598685
cat >expected <<EOF
599686
file:foo mmap bar_mmap
600687
EOF

0 commit comments

Comments
 (0)