git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

From
Taylor Blau <me@ttaylorr.com>
Date
May 5, 2018, 04:03 UTC
Message-ID
<c8b527c5de3b0e5422d2c1afb91d454d1e46fff4.1525492696.git.me@ttaylorr.com>
In-Reply-To
<cover.1525492696.git.me@ttaylorr.com>

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@ttaylorr.com>
---
 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
Previous: Taylor BlauNext: Ævar Arnfjörð Bjarmason
Message 5 of 14 in “builtin/grep.c: teach '-o', '--only-matching'”
  1. 0/2 builtin/grep.c: teach '-o', '--only-matching'Taylor Blau, May 5, 2018
  2. 1/2 grep.c: extract show_line_header()Taylor Blau, May 5, 2018
  3. Eric SunshineMay 5, 2018
  4. Taylor BlauMay 8, 2018
  5. 2/2 builtin/grep.c: teach '-o', '--only-matching' to 'git-grep'Taylor Blau, May 5, 2018
  6. Ævar Arnfjörð BjarmasonMay 5, 2018
  7. Jeff KingMay 8, 2018
  8. Taylor BlauMay 10, 2018
  9. Jeff KingMay 10, 2018
  10. Eric SunshineMay 5, 2018
  11. Taylor BlauMay 8, 2018
  12. 0/2 builtin/grep.c: teach '-o', '--only-matching'Taylor Blau, May 12, 2018
  13. 1/2 grep.c: extract show_line_header()Taylor Blau, May 12, 2018
  14. 2/2 builtin/grep.c: teach '-o', '--only-matching' to 'git-grep'Taylor Blau, May 12, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.