Code

grep: teach --untracked and --exclude-standard options
authorJunio C Hamano <gitster@pobox.com>
Tue, 27 Sep 2011 20:43:12 +0000 (13:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 01:33:45 +0000 (18:33 -0700)
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>
Documentation/git-grep.txt
builtin-grep.c
t/t7002-grep.sh

index e019e760b4b4d58dfbe8819941947008319aedac..6269007b9449aea8a762849b28a80561174e4438 100644 (file)
@@ -9,7 +9,7 @@ git-grep - Print lines matching a pattern
 SYNOPSIS
 --------
 [verse]
-'git grep' [--cached]
+'git grep' [--cached] [--untracked] [--exclude-standard]
           [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp]
@@ -36,6 +36,19 @@ OPTIONS
        Instead of searching in the working tree files, check
        the blobs registered in the index file.
 
+--untracked::
+       In addition to searching in the tracked files in the working
+       tree, search also in untracked files.
+
+--no-exclude-standard::
+       Also search in ignored files by not honoring the `.gitignore`
+       mechanism. Only useful with `--untracked`.
+
+--exclude-standard::
+       Do not pay attention to ignored files specified via the `.gitignore`
+       mechanism.  Only useful when searching files in the current directory
+       with `--no-index`.
+
 -a::
 --text::
        Process binary files as if they were text.
index a10946db3c49797ef717734924416df0879b55a1..2da6bc61b66eea3594b3d0321de4a39cc3f49f8f 100644 (file)
@@ -646,12 +646,14 @@ static int grep_object(struct grep_opt *opt, const char **paths,
        die("unable to grep from object of type %s", typename(obj->type));
 }
 
-static int grep_directory(struct grep_opt *opt, const char **paths)
+static int grep_directory(struct grep_opt *opt, const char **paths, int exc_std)
 {
        struct dir_struct dir;
        int i, hit = 0;
 
        memset(&dir, 0, sizeof(dir));
+       if (exc_std)
+               setup_standard_excludes(&dir);
 
        fill_directory(&dir, paths);
        for (i = 0; i < dir.nr; i++) {
@@ -749,7 +751,7 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
-       int cached = 0;
+       int cached = 0, untracked = 0, opt_exclude = -1;
        int seen_dashdash = 0;
        int external_grep_allowed__ignored;
        struct grep_opt opt;
@@ -764,6 +766,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                { OPTION_BOOLEAN, 0, "index", &use_index, NULL,
                        "finds in contents not managed by git",
                        PARSE_OPT_NOARG | PARSE_OPT_NEGHELP },
+               OPT_BOOLEAN(0, "untracked", &untracked,
+                       "search in both tracked and untracked files"),
+               OPT_SET_INT(0, "exclude-standard", &opt_exclude,
+                           "search also in ignored files", 1),
                OPT_GROUP(""),
                OPT_BOOLEAN('v', "invert-match", &opt.invert,
                        "show non-matching lines"),
@@ -950,18 +956,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                paths[1] = NULL;
        }
 
-       if (!use_index) {
+       if (!use_index && (untracked || cached))
+               die("--cached or --untracked cannot be used with --no-index.");
+
+       if (!use_index || untracked) {
                int hit;
-               if (cached)
-                       die("--cached cannot be used with --no-index.");
+               int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
                if (list.nr)
-                       die("--no-index cannot be used with revs.");
-               hit = grep_directory(&opt, paths);
+                       die("--no-index or --untracked cannot be used with revs.");
+               hit = grep_directory(&opt, paths, use_exclude);
                if (use_threads)
                        hit |= wait_all();
                return !hit;
        }
 
+       if (0 <= opt_exclude)
+               die("--exclude or --no-exclude cannot be used for tracked contents.");
+
        if (!list.nr) {
                int hit;
                if (!cached)
index 918d33f7d1ab4d4d44dd6b13ca7e6029fe148ad5..bf7877d93051eddf96da3e4cb7ebc27a760ba910 100755 (executable)
@@ -455,6 +455,23 @@ test_expect_success 'outside of git repository' '
                test_must_fail git grep o &&
                git grep --no-index o >../../actual.sub &&
                test_cmp ../../expect.sub ../../actual.sub
+       ) &&
+
+       echo ".*o*" >non/git/.gitignore &&
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_must_fail git grep o &&
+               git grep --no-index --exclude-standard o >../actual.full &&
+               test_cmp ../expect.full ../actual.full &&
+
+               {
+                       echo ".gitignore:.*o*"
+                       cat ../expect.full
+               } >../expect.with.ignored &&
+               git grep --no-index --no-exclude o >../actual.full &&
+               test_cmp ../expect.with.ignored ../actual.full
        )
 '
 
@@ -465,9 +482,12 @@ test_expect_success 'inside git repository but with --no-index' '
        echo world >is/git/sub/file2 &&
        echo ".*o*" >is/git/.gitignore &&
        {
-               echo ".gitignore:.*o*" &&
                echo file1:hello &&
                echo sub/file2:world
+       } >is/expect.unignored &&
+       {
+               echo ".gitignore:.*o*" &&
+               cat is/expect.unignored
        } >is/expect.full &&
        : >is/expect.empty &&
        echo file2:world >is/expect.sub
@@ -476,12 +496,24 @@ test_expect_success 'inside git repository but with --no-index' '
                git init &&
                test_must_fail git grep o >../actual.full &&
                test_cmp ../expect.empty ../actual.full &&
+
+               git grep --untracked o >../actual.unignored &&
+               test_cmp ../expect.unignored ../actual.unignored &&
+
                git grep --no-index o >../actual.full &&
                test_cmp ../expect.full ../actual.full &&
+
+               git grep --no-index --exclude-standard o >../actual.unignored &&
+               test_cmp ../expect.unignored ../actual.unignored &&
+
                cd sub &&
                test_must_fail git grep o >../../actual.sub &&
                test_cmp ../../expect.empty ../../actual.sub &&
+
                git grep --no-index o >../../actual.sub &&
+               test_cmp ../../expect.sub ../../actual.sub &&
+
+               git grep --untracked o >../../actual.sub &&
                test_cmp ../../expect.sub ../../actual.sub
        )
 '