Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'jc/maint-log-grep-all-match-1' into maint

* jc/maint-log-grep-all-match-1:
  grep.c: make two symbols really file-scope static this time
  t7810-grep: test --all-match with multiple --grep and --author options
  t7810-grep: test interaction of multiple --grep and --author options
  t7810-grep: test multiple --author with --all-match
  t7810-grep: test multiple --grep with and without --all-match
  t7810-grep: bring log --grep tests in common form
  grep.c: mark private file-scope symbols as static
  log: document use of multiple commit limiting options
  log --grep/--author: honor --all-match honored for multiple --grep patterns
  grep: show --debug output only once
  grep: teach --debug option to dump the parse tree
  • Loading branch information...
commit 31d69db340d3d056668cbc278abf01a0a552d9b4 2 parents 52938b1 + 3083301
Junio C Hamano authored September 29, 2012
23  Documentation/rev-list-options.txt
@@ -3,8 +3,15 @@ Commit Limiting
3 3
 
4 4
 Besides specifying a range of commits that should be listed using the
5 5
 special notations explained in the description, additional commit
6  
-limiting may be applied. Note that they are applied before commit
7  
-ordering and formatting options, such as '--reverse'.
  6
+limiting may be applied.
  7
+
  8
+Using more options generally further limits the output (e.g.
  9
+`--since=<date1>` limits to commits newer than `<date1>`, and using it
  10
+with `--grep=<pattern>` further limits to commits whose log message
  11
+has a line that matches `<pattern>`), unless otherwise noted.
  12
+
  13
+Note that these are applied before commit
  14
+ordering and formatting options, such as `--reverse`.
8 15
 
9 16
 --
10 17
 
@@ -39,16 +46,22 @@ endif::git-rev-list[]
39 46
 --committer=<pattern>::
40 47
 
41 48
 	Limit the commits output to ones with author/committer
42  
-	header lines that match the specified pattern (regular expression).
  49
+	header lines that match the specified pattern (regular
  50
+	expression).  With more than one `--author=<pattern>`,
  51
+	commits whose author matches any of the given patterns are
  52
+	chosen (similarly for multiple `--committer=<pattern>`).
43 53
 
44 54
 --grep=<pattern>::
45 55
 
46 56
 	Limit the commits output to ones with log message that
47  
-	matches the specified pattern (regular expression).
  57
+	matches the specified pattern (regular expression).  With
  58
+	more than one `--grep=<pattern>`, commits whose message
  59
+	matches any of the given patterns are chosen (but see
  60
+	`--all-match`).
48 61
 
49 62
 --all-match::
50 63
 	Limit the commits output to ones that match all given --grep,
51  
-	--author and --committer instead of ones that match at least one.
  64
+	instead of ones that match at least one.
52 65
 
53 66
 -i::
54 67
 --regexp-ignore-case::
4  builtin/grep.c
@@ -209,6 +209,7 @@ static void start_threads(struct grep_opt *opt)
209 209
 		int err;
210 210
 		struct grep_opt *o = grep_opt_dup(opt);
211 211
 		o->output = strbuf_out;
  212
+		o->debug = 0;
212 213
 		compile_grep_patterns(o);
213 214
 		err = pthread_create(&threads[i], NULL, run, o);
214 215
 
@@ -772,6 +773,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
772 773
 			   "indicate hit with exit status without output"),
773 774
 		OPT_BOOLEAN(0, "all-match", &opt.all_match,
774 775
 			"show only matches from files that match all patterns"),
  776
+		{ OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
  777
+		  "show parse tree for grep expression",
  778
+		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
775 779
 		OPT_GROUP(""),
776 780
 		{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
777 781
 			"pager", "show matching files in the pager",
119  grep.c
@@ -3,6 +3,10 @@
3 3
 #include "userdiff.h"
4 4
 #include "xdiff-interface.h"
5 5
 
  6
+static int grep_source_load(struct grep_source *gs);
  7
+static int grep_source_is_binary(struct grep_source *gs);
  8
+
  9
+
6 10
 static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
7 11
 					const char *origin, int no,
8 12
 					enum grep_pat_token t,
@@ -332,6 +336,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
332 336
 	return compile_pattern_or(list);
333 337
 }
334 338
 
  339
+static void indent(int in)
  340
+{
  341
+	while (in-- > 0)
  342
+		fputc(' ', stderr);
  343
+}
  344
+
  345
+static void dump_grep_pat(struct grep_pat *p)
  346
+{
  347
+	switch (p->token) {
  348
+	case GREP_AND: fprintf(stderr, "*and*"); break;
  349
+	case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
  350
+	case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
  351
+	case GREP_NOT: fprintf(stderr, "*not*"); break;
  352
+	case GREP_OR: fprintf(stderr, "*or*"); break;
  353
+
  354
+	case GREP_PATTERN: fprintf(stderr, "pattern"); break;
  355
+	case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
  356
+	case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
  357
+	}
  358
+
  359
+	switch (p->token) {
  360
+	default: break;
  361
+	case GREP_PATTERN_HEAD:
  362
+		fprintf(stderr, "<head %d>", p->field); break;
  363
+	case GREP_PATTERN_BODY:
  364
+		fprintf(stderr, "<body>"); break;
  365
+	}
  366
+	switch (p->token) {
  367
+	default: break;
  368
+	case GREP_PATTERN_HEAD:
  369
+	case GREP_PATTERN_BODY:
  370
+	case GREP_PATTERN:
  371
+		fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
  372
+		break;
  373
+	}
  374
+	fputc('\n', stderr);
  375
+}
  376
+
  377
+static void dump_grep_expression_1(struct grep_expr *x, int in)
  378
+{
  379
+	indent(in);
  380
+	switch (x->node) {
  381
+	case GREP_NODE_TRUE:
  382
+		fprintf(stderr, "true\n");
  383
+		break;
  384
+	case GREP_NODE_ATOM:
  385
+		dump_grep_pat(x->u.atom);
  386
+		break;
  387
+	case GREP_NODE_NOT:
  388
+		fprintf(stderr, "(not\n");
  389
+		dump_grep_expression_1(x->u.unary, in+1);
  390
+		indent(in);
  391
+		fprintf(stderr, ")\n");
  392
+		break;
  393
+	case GREP_NODE_AND:
  394
+		fprintf(stderr, "(and\n");
  395
+		dump_grep_expression_1(x->u.binary.left, in+1);
  396
+		dump_grep_expression_1(x->u.binary.right, in+1);
  397
+		indent(in);
  398
+		fprintf(stderr, ")\n");
  399
+		break;
  400
+	case GREP_NODE_OR:
  401
+		fprintf(stderr, "(or\n");
  402
+		dump_grep_expression_1(x->u.binary.left, in+1);
  403
+		dump_grep_expression_1(x->u.binary.right, in+1);
  404
+		indent(in);
  405
+		fprintf(stderr, ")\n");
  406
+		break;
  407
+	}
  408
+}
  409
+
  410
+static void dump_grep_expression(struct grep_opt *opt)
  411
+{
  412
+	struct grep_expr *x = opt->pattern_expression;
  413
+
  414
+	if (opt->all_match)
  415
+		fprintf(stderr, "[all-match]\n");
  416
+	dump_grep_expression_1(x, 0);
  417
+	fflush(NULL);
  418
+}
  419
+
335 420
 static struct grep_expr *grep_true_expr(void)
336 421
 {
337 422
 	struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -395,7 +480,23 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
395 480
 	return header_expr;
396 481
 }
397 482
 
398  
-void compile_grep_patterns(struct grep_opt *opt)
  483
+static struct grep_expr *grep_splice_or(struct grep_expr *x, struct grep_expr *y)
  484
+{
  485
+	struct grep_expr *z = x;
  486
+
  487
+	while (x) {
  488
+		assert(x->node == GREP_NODE_OR);
  489
+		if (x->u.binary.right &&
  490
+		    x->u.binary.right->node == GREP_NODE_TRUE) {
  491
+			x->u.binary.right = y;
  492
+			break;
  493
+		}
  494
+		x = x->u.binary.right;
  495
+	}
  496
+	return z;
  497
+}
  498
+
  499
+static void compile_grep_patterns_real(struct grep_opt *opt)
399 500
 {
400 501
 	struct grep_pat *p;
401 502
 	struct grep_expr *header_expr = prep_header_patterns(opt);
@@ -415,7 +516,7 @@ void compile_grep_patterns(struct grep_opt *opt)
415 516
 
416 517
 	if (opt->all_match || header_expr)
417 518
 		opt->extended = 1;
418  
-	else if (!opt->extended)
  519
+	else if (!opt->extended && !opt->debug)
419 520
 		return;
420 521
 
421 522
 	p = opt->pattern_list;
@@ -429,12 +530,22 @@ void compile_grep_patterns(struct grep_opt *opt)
429 530
 
430 531
 	if (!opt->pattern_expression)
431 532
 		opt->pattern_expression = header_expr;
  533
+	else if (opt->all_match)
  534
+		opt->pattern_expression = grep_splice_or(header_expr,
  535
+							 opt->pattern_expression);
432 536
 	else
433 537
 		opt->pattern_expression = grep_or_expr(opt->pattern_expression,
434 538
 						       header_expr);
435 539
 	opt->all_match = 1;
436 540
 }
437 541
 
  542
+void compile_grep_patterns(struct grep_opt *opt)
  543
+{
  544
+	compile_grep_patterns_real(opt);
  545
+	if (opt->debug)
  546
+		dump_grep_expression(opt);
  547
+}
  548
+
438 549
 static void free_pattern_expr(struct grep_expr *x)
439 550
 {
440 551
 	switch (x->node) {
@@ -1358,7 +1469,7 @@ static int grep_source_load_file(struct grep_source *gs)
1358 1469
 	return 0;
1359 1470
 }
1360 1471
 
1361  
-int grep_source_load(struct grep_source *gs)
  1472
+static int grep_source_load(struct grep_source *gs)
1362 1473
 {
1363 1474
 	if (gs->buf)
1364 1475
 		return 0;
@@ -1386,7 +1497,7 @@ void grep_source_load_driver(struct grep_source *gs)
1386 1497
 	grep_attr_unlock();
1387 1498
 }
1388 1499
 
1389  
-int grep_source_is_binary(struct grep_source *gs)
  1500
+static int grep_source_is_binary(struct grep_source *gs)
1390 1501
 {
1391 1502
 	grep_source_load_driver(gs);
1392 1503
 	if (gs->driver->binary != -1)
4  grep.h
@@ -90,6 +90,7 @@ struct grep_opt {
90 90
 	int word_regexp;
91 91
 	int fixed;
92 92
 	int all_match;
  93
+	int debug;
93 94
 #define GREP_BINARY_DEFAULT	0
94 95
 #define GREP_BINARY_NOMATCH	1
95 96
 #define GREP_BINARY_TEXT	2
@@ -148,11 +149,10 @@ struct grep_source {
148 149
 
149 150
 void grep_source_init(struct grep_source *gs, enum grep_source_type type,
150 151
 		      const char *name, const void *identifier);
151  
-int grep_source_load(struct grep_source *gs);
152 152
 void grep_source_clear_data(struct grep_source *gs);
153 153
 void grep_source_clear(struct grep_source *gs);
154 154
 void grep_source_load_driver(struct grep_source *gs);
155  
-int grep_source_is_binary(struct grep_source *gs);
  155
+
156 156
 
157 157
 int grep_source(struct grep_opt *opt, struct grep_source *gs);
158 158
 
2  revision.c
@@ -1598,6 +1598,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1598 1598
 	} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
1599 1599
 		add_message_grep(revs, optarg);
1600 1600
 		return argcount;
  1601
+	} else if (!strcmp(arg, "--grep-debug")) {
  1602
+		revs->grep_filter.debug = 1;
1601 1603
 	} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
1602 1604
 		revs->grep_filter.regflags |= REG_EXTENDED;
1603 1605
 	} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
90  t/t7810-grep.sh
@@ -424,31 +424,41 @@ test_expect_success 'log grep setup' '
424 424
 
425 425
 test_expect_success 'log grep (1)' '
426 426
 	git log --author=author --pretty=tformat:%s >actual &&
427  
-	( echo third ; echo initial ) >expect &&
  427
+	{
  428
+		echo third && echo initial
  429
+	} >expect &&
428 430
 	test_cmp expect actual
429 431
 '
430 432
 
431 433
 test_expect_success 'log grep (2)' '
432 434
 	git log --author=" * " -F --pretty=tformat:%s >actual &&
433  
-	( echo second ) >expect &&
  435
+	{
  436
+		echo second
  437
+	} >expect &&
434 438
 	test_cmp expect actual
435 439
 '
436 440
 
437 441
 test_expect_success 'log grep (3)' '
438 442
 	git log --author="^A U" --pretty=tformat:%s >actual &&
439  
-	( echo third ; echo initial ) >expect &&
  443
+	{
  444
+		echo third && echo initial
  445
+	} >expect &&
440 446
 	test_cmp expect actual
441 447
 '
442 448
 
443 449
 test_expect_success 'log grep (4)' '
444 450
 	git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
445  
-	( echo second ) >expect &&
  451
+	{
  452
+		echo second
  453
+	} >expect &&
446 454
 	test_cmp expect actual
447 455
 '
448 456
 
449 457
 test_expect_success 'log grep (5)' '
450 458
 	git log --author=Thor -F --pretty=tformat:%s >actual &&
451  
-	( echo third ; echo initial ) >expect &&
  459
+	{
  460
+		echo third && echo initial
  461
+	} >expect &&
452 462
 	test_cmp expect actual
453 463
 '
454 464
 
@@ -458,11 +468,19 @@ test_expect_success 'log grep (6)' '
458 468
 	test_cmp expect actual
459 469
 '
460 470
 
461  
-test_expect_success 'log --grep --author implicitly uses all-match' '
462  
-	# grep matches initial and second but not third
463  
-	# author matches only initial and third
464  
-	git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
465  
-	echo initial >expect &&
  471
+test_expect_success 'log with multiple --grep uses union' '
  472
+	git log --grep=i --grep=r --format=%s >actual &&
  473
+	{
  474
+		echo fourth && echo third && echo initial
  475
+	} >expect &&
  476
+	test_cmp expect actual
  477
+'
  478
+
  479
+test_expect_success 'log --all-match with multiple --grep uses intersection' '
  480
+	git log --all-match --grep=i --grep=r --format=%s >actual &&
  481
+	{
  482
+		echo third
  483
+	} >expect &&
466 484
 	test_cmp expect actual
467 485
 '
468 486
 
@@ -474,7 +492,47 @@ test_expect_success 'log with multiple --author uses union' '
474 492
 	test_cmp expect actual
475 493
 '
476 494
 
477  
-test_expect_success 'log with --grep and multiple --author uses all-match' '
  495
+test_expect_success 'log --all-match with multiple --author still uses union' '
  496
+	git log --all-match --author="Thor" --author="Aster" --format=%s >actual &&
  497
+	{
  498
+	    echo third && echo second && echo initial
  499
+	} >expect &&
  500
+	test_cmp expect actual
  501
+'
  502
+
  503
+test_expect_success 'log --grep --author uses intersection' '
  504
+	# grep matches only third and fourth
  505
+	# author matches only initial and third
  506
+	git log --author="A U Thor" --grep=r --format=%s >actual &&
  507
+	{
  508
+		echo third
  509
+	} >expect &&
  510
+	test_cmp expect actual
  511
+'
  512
+
  513
+test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
  514
+	# grep matches initial and second but not third
  515
+	# author matches only initial and third
  516
+	git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
  517
+	{
  518
+		echo initial
  519
+	} >expect &&
  520
+	test_cmp expect actual
  521
+'
  522
+
  523
+test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
  524
+	# grep matches only initial and third
  525
+	# author matches all but second
  526
+	git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&
  527
+	{
  528
+	    echo third && echo initial
  529
+	} >expect &&
  530
+	test_cmp expect actual
  531
+'
  532
+
  533
+test_expect_success 'log --grep --author --author takes union of authors and intersects with grep' '
  534
+	# grep matches only initial and third
  535
+	# author matches all but second
478 536
 	git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
479 537
 	{
480 538
 	    echo third && echo initial
@@ -482,9 +540,13 @@ test_expect_success 'log with --grep and multiple --author uses all-match' '
482 540
 	test_cmp expect actual
483 541
 '
484 542
 
485  
-test_expect_success 'log with --grep and multiple --author uses all-match' '
486  
-	git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
487  
-	>expect &&
  543
+test_expect_success 'log --all-match --grep --grep --author takes intersection' '
  544
+	# grep matches only third
  545
+	# author matches only initial and third
  546
+	git log --all-match --author="A U Thor" --grep=i --grep=r --format=%s >actual &&
  547
+	{
  548
+		echo third
  549
+	} >expect &&
488 550
 	test_cmp expect actual
489 551
 '
490 552
 

0 notes on commit 31d69db

Please sign in to comment.
Something went wrong with that request. Please try again.