grep: teach --untracked and --exclude-standard options

In a working tree of a git managed repository, "grep --untracked" would find the specified patterns from files in untracked files in addition to its usual behaviour of finding them in the tracked files. By default, when working with "--no-index" option, "grep" does not pay attention to .gitignore mechanism. "grep --no-index --exclude-standard" can be used to tell the command to use .gitignore and stop reporting hits from files that would be ignored. Also, when working without "--no-index", "grep" honors .gitignore mechanism, and "grep --no-exclude-standard" can be used to tell the command to include hits from files that are ignored. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 27, 2011 at 13:43 UTC 0a93fb8a9cc6fa1bf7dba6b498cba3f57f5017a1
3 files changed +65 -9
Documentation/git-grep.txt
+14 -1
@@ -9,7 +9,7 @@ git-grep - Print lines matching a pattern
9 SYNOPSIS
10 --------
11 [verse]
12 -'git grep' [--cached]
12 +'git grep' [--cached] [--untracked] [--exclude-standard]
13 [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
14 [-v | --invert-match] [-h|-H] [--full-name]
15 [-E | --extended-regexp] [-G | --basic-regexp]
@@ -36,6 +36,19 @@ OPTIONS
36 Instead of searching in the working tree files, check
37 the blobs registered in the index file.
38
39 +--untracked::
40 + In addition to searching in the tracked files in the working
41 + tree, search also in untracked files.
42 +
43 +--no-exclude-standard::
44 + Also search in ignored files by not honoring the `.gitignore`
45 + mechanism. Only useful with `--untracked`.
46 +
47 +--exclude-standard::
48 + Do not pay attention to ignored files specified via the `.gitignore`
49 + mechanism. Only useful when searching files in the current directory
50 + with `--no-index`.
51 +
52 -a::
53 --text::
54 Process binary files as if they were text.
builtin-grep.c
+18 -7
@@ -646,12 +646,14 @@ static int grep_object(struct grep_opt *opt, const char **paths,
646 die("unable to grep from object of type %s", typename(obj->type));
647 }
648
649 -static int grep_directory(struct grep_opt *opt, const char **paths)
649 +static int grep_directory(struct grep_opt *opt, const char **paths, int exc_std)
650 {
651 struct dir_struct dir;
652 int i, hit = 0;
653
654 memset(&dir, 0, sizeof(dir));
655 + if (exc_std)
656 + setup_standard_excludes(&dir);
657
658 fill_directory(&dir, paths);
659 for (i = 0; i < dir.nr; i++) {
@@ -749,7 +751,7 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
751 int cmd_grep(int argc, const char **argv, const char *prefix)
752 {
753 int hit = 0;
752 - int cached = 0;
754 + int cached = 0, untracked = 0, opt_exclude = -1;
755 int seen_dashdash = 0;
756 int external_grep_allowed__ignored;
757 struct grep_opt opt;
@@ -764,6 +766,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
766 { OPTION_BOOLEAN, 0, "index", &use_index, NULL,
767 "finds in contents not managed by git",
768 PARSE_OPT_NOARG | PARSE_OPT_NEGHELP },
769 + OPT_BOOLEAN(0, "untracked", &untracked,
770 + "search in both tracked and untracked files"),
771 + OPT_SET_INT(0, "exclude-standard", &opt_exclude,
772 + "search also in ignored files", 1),
773 OPT_GROUP(""),
774 OPT_BOOLEAN('v', "invert-match", &opt.invert,
775 "show non-matching lines"),
@@ -950,18 +956,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
956 paths[1] = NULL;
957 }
958
953 - if (!use_index) {
959 + if (!use_index && (untracked || cached))
960 + die("--cached or --untracked cannot be used with --no-index.");
961 +
962 + if (!use_index || untracked) {
963 int hit;
955 - if (cached)
956 - die("--cached cannot be used with --no-index.");
964 + int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
965 if (list.nr)
958 - die("--no-index cannot be used with revs.");
959 - hit = grep_directory(&opt, paths);
966 + die("--no-index or --untracked cannot be used with revs.");
967 + hit = grep_directory(&opt, paths, use_exclude);
968 if (use_threads)
969 hit |= wait_all();
970 return !hit;
971 }
972
973 + if (0 <= opt_exclude)
974 + die("--exclude or --no-exclude cannot be used for tracked contents.");
975 +
976 if (!list.nr) {
977 int hit;
978 if (!cached)
t/t7002-grep.sh
+33 -1
@@ -455,6 +455,23 @@ test_expect_success 'outside of git repository' '
455 test_must_fail git grep o &&
456 git grep --no-index o >../../actual.sub &&
457 test_cmp ../../expect.sub ../../actual.sub
458 + ) &&
459 +
460 + echo ".*o*" >non/git/.gitignore &&
461 + (
462 + GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
463 + export GIT_CEILING_DIRECTORIES &&
464 + cd non/git &&
465 + test_must_fail git grep o &&
466 + git grep --no-index --exclude-standard o >../actual.full &&
467 + test_cmp ../expect.full ../actual.full &&
468 +
469 + {
470 + echo ".gitignore:.*o*"
471 + cat ../expect.full
472 + } >../expect.with.ignored &&
473 + git grep --no-index --no-exclude o >../actual.full &&
474 + test_cmp ../expect.with.ignored ../actual.full
475 )
476 '
477
@@ -465,9 +482,12 @@ test_expect_success 'inside git repository but with --no-index' '
482 echo world >is/git/sub/file2 &&
483 echo ".*o*" >is/git/.gitignore &&
484 {
468 - echo ".gitignore:.*o*" &&
485 echo file1:hello &&
486 echo sub/file2:world
487 + } >is/expect.unignored &&
488 + {
489 + echo ".gitignore:.*o*" &&
490 + cat is/expect.unignored
491 } >is/expect.full &&
492 : >is/expect.empty &&
493 echo file2:world >is/expect.sub
@@ -476,12 +496,24 @@ test_expect_success 'inside git repository but with --no-index' '
496 git init &&
497 test_must_fail git grep o >../actual.full &&
498 test_cmp ../expect.empty ../actual.full &&
499 +
500 + git grep --untracked o >../actual.unignored &&
501 + test_cmp ../expect.unignored ../actual.unignored &&
502 +
503 git grep --no-index o >../actual.full &&
504 test_cmp ../expect.full ../actual.full &&
505 +
506 + git grep --no-index --exclude-standard o >../actual.unignored &&
507 + test_cmp ../expect.unignored ../actual.unignored &&
508 +
509 cd sub &&
510 test_must_fail git grep o >../../actual.sub &&
511 test_cmp ../../expect.empty ../../actual.sub &&
512 +
513 git grep --no-index o >../../actual.sub &&
514 + test_cmp ../../expect.sub ../../actual.sub &&
515 +
516 + git grep --untracked o >../../actual.sub &&
517 test_cmp ../../expect.sub ../../actual.sub
518 )
519 '