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>
This commit is contained in:
Junio C Hamano 2011-09-27 13:43:12 -07:00
parent a9e643668a
commit 0a93fb8a9c
3 changed files with 65 additions and 9 deletions

View file

@ -9,7 +9,7 @@ git-grep - Print lines matching a pattern
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git grep' [--cached] 'git grep' [--cached] [--untracked] [--exclude-standard]
[-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp] [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
[-v | --invert-match] [-h|-H] [--full-name] [-v | --invert-match] [-h|-H] [--full-name]
[-E | --extended-regexp] [-G | --basic-regexp] [-E | --extended-regexp] [-G | --basic-regexp]
@ -36,6 +36,19 @@ OPTIONS
Instead of searching in the working tree files, check Instead of searching in the working tree files, check
the blobs registered in the index file. 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:: -a::
--text:: --text::
Process binary files as if they were text. Process binary files as if they were text.

View 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)); 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; struct dir_struct dir;
int i, hit = 0; int i, hit = 0;
memset(&dir, 0, sizeof(dir)); memset(&dir, 0, sizeof(dir));
if (exc_std)
setup_standard_excludes(&dir);
fill_directory(&dir, paths); fill_directory(&dir, paths);
for (i = 0; i < dir.nr; i++) { 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 cmd_grep(int argc, const char **argv, const char *prefix)
{ {
int hit = 0; int hit = 0;
int cached = 0; int cached = 0, untracked = 0, opt_exclude = -1;
int seen_dashdash = 0; int seen_dashdash = 0;
int external_grep_allowed__ignored; int external_grep_allowed__ignored;
struct grep_opt opt; 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, { OPTION_BOOLEAN, 0, "index", &use_index, NULL,
"finds in contents not managed by git", "finds in contents not managed by git",
PARSE_OPT_NOARG | PARSE_OPT_NEGHELP }, 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_GROUP(""),
OPT_BOOLEAN('v', "invert-match", &opt.invert, OPT_BOOLEAN('v', "invert-match", &opt.invert,
"show non-matching lines"), "show non-matching lines"),
@ -950,18 +956,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
paths[1] = NULL; 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; int hit;
if (cached) int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
die("--cached cannot be used with --no-index.");
if (list.nr) if (list.nr)
die("--no-index cannot be used with revs."); die("--no-index or --untracked cannot be used with revs.");
hit = grep_directory(&opt, paths); hit = grep_directory(&opt, paths, use_exclude);
if (use_threads) if (use_threads)
hit |= wait_all(); hit |= wait_all();
return !hit; return !hit;
} }
if (0 <= opt_exclude)
die("--exclude or --no-exclude cannot be used for tracked contents.");
if (!list.nr) { if (!list.nr) {
int hit; int hit;
if (!cached) if (!cached)

View file

@ -455,6 +455,23 @@ test_expect_success 'outside of git repository' '
test_must_fail git grep o && test_must_fail git grep o &&
git grep --no-index o >../../actual.sub && git grep --no-index o >../../actual.sub &&
test_cmp ../../expect.sub ../../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 world >is/git/sub/file2 &&
echo ".*o*" >is/git/.gitignore && echo ".*o*" >is/git/.gitignore &&
{ {
echo ".gitignore:.*o*" &&
echo file1:hello && echo file1:hello &&
echo sub/file2:world echo sub/file2:world
} >is/expect.unignored &&
{
echo ".gitignore:.*o*" &&
cat is/expect.unignored
} >is/expect.full && } >is/expect.full &&
: >is/expect.empty && : >is/expect.empty &&
echo file2:world >is/expect.sub echo file2:world >is/expect.sub
@ -476,12 +496,24 @@ test_expect_success 'inside git repository but with --no-index' '
git init && git init &&
test_must_fail git grep o >../actual.full && test_must_fail git grep o >../actual.full &&
test_cmp ../expect.empty ../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 && git grep --no-index o >../actual.full &&
test_cmp ../expect.full ../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 && cd sub &&
test_must_fail git grep o >../../actual.sub && test_must_fail git grep o >../../actual.sub &&
test_cmp ../../expect.empty ../../actual.sub && test_cmp ../../expect.empty ../../actual.sub &&
git grep --no-index o >../../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 test_cmp ../../expect.sub ../../actual.sub
) )
' '