[PATCH 2/2] builtin/grep.c: teach '-o', '--only-matching' to 'git-grep'

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

 



Teach GNU grep(1)'s '-o' ('--only-matching') to 'git-grep'. This option
prints only the matching components of each line. It writes multiple
lines if more than one match exists on a given line.

For example:

  $ git grep -on --column --heading git -- README.md | head -3
  README.md
  15:56:git
  18:20:git

By using show_line_header(), 'git grep --only-matching' correctly
respects the '--header' option:

  $ git grep -on --column --heading git -- README.md | head -4
  README.md
  15:56:git
  18:20:git
  19:16:git

Signed-off-by: Taylor Blau <me@xxxxxxxxxxxx>
---
 Documentation/git-grep.txt |  6 +++++-
 builtin/grep.c             |  1 +
 grep.c                     | 23 ++++++++++++++++++++---
 grep.h                     |  1 +
 t/t7810-grep.sh            | 33 +++++++++++++++++++++++++++++++++
 5 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index d451cd8883..9754923041 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -20,7 +20,7 @@ SYNOPSIS
 	   [-c | --count] [--all-match] [-q | --quiet]
 	   [--max-depth <depth>]
 	   [--color[=<when>] | --no-color]
-	   [--break] [--heading] [-p | --show-function]
+	   [--break] [--heading] [-o | --only-matching] [-p | --show-function]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
 	   [-W | --function-context]
 	   [--threads <num>]
@@ -221,6 +221,10 @@ providing this option will cause it to die.
 	Show the filename above the matches in that file instead of
 	at the start of each shown line.
 
+--o::
+--only-matching::
+	Show only the matching part of the lines.
+
 -p::
 --show-function::
 	Show the preceding line that contains the function name of
diff --git a/builtin/grep.c b/builtin/grep.c
index 5c83f17759..5028bf96cf 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -851,6 +851,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			N_("print empty line between matches from different files")),
 		OPT_BOOL(0, "heading", &opt.heading,
 			N_("show filename only once above matches from same file")),
+		OPT_BOOL('o', "only-matching", &opt.only_matching, N_("show only matches")),
 		OPT_GROUP(""),
 		OPT_CALLBACK('C', "context", &opt, N_("n"),
 			N_("show <n> context lines before and after matches"),
diff --git a/grep.c b/grep.c
index 89dd719e4d..da3f8e6266 100644
--- a/grep.c
+++ b/grep.c
@@ -1422,11 +1422,13 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
 		}
 	}
 	show_line_header(opt, name, lno, cno, sign);
-	if (opt->color) {
+	if (opt->color || opt->only_matching) {
 		regmatch_t match;
 		enum grep_context ctx = GREP_CONTEXT_BODY;
 		int ch = *eol;
 		int eflags = 0;
+		int first = 1;
+		int offset = 1;
 
 		if (sign == ':')
 			match_color = opt->color_match_selected;
@@ -1443,16 +1445,31 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
 			if (match.rm_so == match.rm_eo)
 				break;
 
-			output_color(opt, bol, match.rm_so, line_color);
+			if (!opt->only_matching)
+				output_color(opt, bol, match.rm_so, line_color);
+			else if (!first) {
+				/*
+				 * We are given --only-matching, and this is not
+				 * the first match on a line. Reprint the
+				 * newline and header before showing another
+				 * match.
+				 */
+				opt->output(opt, "\n", 1);
+				show_line_header(opt, name, lno,
+					offset+match.rm_so, sign);
+			}
 			output_color(opt, bol + match.rm_so,
 				     match.rm_eo - match.rm_so, match_color);
+			offset += match.rm_eo;
 			bol += match.rm_eo;
 			rest -= match.rm_eo;
 			eflags = REG_NOTBOL;
+			first = 0;
 		}
 		*eol = ch;
 	}
-	output_color(opt, bol, rest, line_color);
+	if (!opt->only_matching)
+		output_color(opt, bol, rest, line_color);
 	opt->output(opt, "\n", 1);
 }
 
diff --git a/grep.h b/grep.h
index 08a0b391c5..24c1460100 100644
--- a/grep.h
+++ b/grep.h
@@ -126,6 +126,7 @@ struct grep_opt {
 	const char *prefix;
 	int prefix_length;
 	regex_t regexp;
+	int only_matching;
 	int linenum;
 	int columnnum;
 	int invert;
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index a03c3416e7..ef7f4ce725 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -1420,6 +1420,39 @@ test_expect_success 'grep --heading' '
 	test_cmp expected actual
 '
 
+cat >expected <<EOF
+file:1:5:mmap
+file:2:5:mmap
+file:3:5:mmap
+file:3:14:mmap
+file:4:5:mmap
+file:4:14:mmap
+file:5:5:mmap
+file:5:14:mmap
+EOF
+
+test_expect_success 'grep --only-matching' '
+	git grep --only-matching --line-number --column mmap file >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file
+1:5:mmap
+2:5:mmap
+3:5:mmap
+3:14:mmap
+4:5:mmap
+4:14:mmap
+5:5:mmap
+5:14:mmap
+EOF
+
+test_expect_success 'grep --only-matching --heading' '
+	git grep --only-matching --heading --line-number --column mmap file >actual &&
+	test_cmp expected actual
+'
+
 cat >expected <<EOF
 <BOLD;GREEN>hello.c<RESET>
 4:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
-- 
2.17.0



[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