From: "Michał Kiedrowicz" <michal.kiedrowicz@gmail.com>
To: Git Mailing List <git@vger.kernel.org>
Cc: "Michał Kiedrowicz" <michal.kiedrowicz@gmail.com>
Subject: [PATCH/RFC] grep: Add --directories option.
Date: Thu, 9 Jul 2009 21:20:28 +0200 [thread overview]
Message-ID: <1247167228-2466-1-git-send-email-michal.kiedrowicz@gmail.com> (raw)
Sometimes it is useful to grep directories non-recursive. E.g. if I want
to look for all files in main directory, but not in any subdirectory.
Or in Documentation/, but not in Documentation/technical/ and so on.
This patch adds support for GNU grep compatible option
"--directories=action" to git-grep. Currently supported actions are:
recurse (default) and skip. Action 'read' is not implemented.
Documentation updates and simple test cases are also provided.
Signed-off-by: Michał Kiedrowicz <michal.kiedrowicz@gmail.com>
---
Documentation/git-grep.txt | 7 ++++
builtin-grep.c | 71 +++++++++++++++++++++++++++++++++----------
t/t7002-grep.sh | 34 ++++++++++++++++++++-
3 files changed, 94 insertions(+), 18 deletions(-)
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index fccb82d..1c4b1ff 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -17,6 +17,7 @@ SYNOPSIS
[-l | --files-with-matches] [-L | --files-without-match]
[-z | --null]
[-c | --count] [--all-match]
+ [(-d|--directories) <action>]
[--color | --no-color]
[-A <post-context>] [-B <pre-context>] [-C <context>]
[-f <file>] [-e] <pattern>
@@ -47,6 +48,12 @@ OPTIONS
-I::
Don't match the pattern in binary files.
+-d <action>::
+--directories=<action>::
+ If an input file is a directory, use `action` to process it. If
+ `action` is recurse (default), read all files under each directory,
+ recursively. If `action` is skip, directories are skipped.
+
-w::
--word-regexp::
Match the pattern only at word boundary (either begin at the
diff --git a/builtin-grep.c b/builtin-grep.c
index e558368..27330e8 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -45,27 +45,34 @@ static int grep_config(const char *var, const char *value, void *cb)
return git_color_default_config(var, value, cb);
}
+static inline int accept_subdir(const char *path, int recurse)
+{
+ return recurse || !strchr(path, '/');
+}
+
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
*/
-static int pathspec_matches(const char **paths, const char *name)
+static int pathspec_matches(const char **paths, const char *name, int recurse)
{
int namelen, i;
if (!paths || !*paths)
- return 1;
+ return accept_subdir(name, recurse);
namelen = strlen(name);
for (i = 0; paths[i]; i++) {
const char *match = paths[i];
int matchlen = strlen(match);
const char *cp, *meta;
- if (!matchlen ||
+ if ((!matchlen && accept_subdir(name, recurse)) ||
((matchlen <= namelen) &&
!strncmp(name, match, matchlen) &&
- (match[matchlen-1] == '/' ||
- name[matchlen] == '\0' || name[matchlen] == '/')))
+ (name[matchlen] == '\0' ||
+ ((match[matchlen-1] == '/'|| name[matchlen] == '/') &&
+ accept_subdir(name + matchlen + 1, recurse))))) {
return 1;
+ }
if (!fnmatch(match, name, 0))
return 1;
if (name[namelen-1] != '/')
@@ -307,7 +314,8 @@ static void grep_add_color(struct strbuf *sb, const char *escape_seq)
strbuf_setlen(sb, sb->len - 1);
}
-static int external_grep(struct grep_opt *opt, const char **paths, int cached)
+static int external_grep(struct grep_opt *opt, const char **paths, int cached,
+ int recurse)
{
int i, nr, argc, hit, len, status;
const char *argv[MAXARGS+1];
@@ -403,7 +411,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
int kept;
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, recurse))
continue;
name = ce->name;
if (name[0] == '-') {
@@ -437,7 +445,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
#endif
static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
- int external_grep_allowed)
+ int external_grep_allowed, int recurse)
{
int hit = 0;
int nr;
@@ -450,7 +458,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
* be a lot more optimized
*/
if (!cached && external_grep_allowed) {
- hit = external_grep(opt, paths, cached);
+ hit = external_grep(opt, paths, cached, recurse);
if (hit >= 0)
return hit;
}
@@ -460,7 +468,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, recurse))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
@@ -488,7 +496,8 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
static int grep_tree(struct grep_opt *opt, const char **paths,
struct tree_desc *tree,
- const char *tree_name, const char *base)
+ const char *tree_name, const char *base,
+ int recurse)
{
int len;
int hit = 0;
@@ -520,7 +529,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
strbuf_addch(&pathbuf, '/');
down = pathbuf.buf + tn_len;
- if (!pathspec_matches(paths, down))
+ if (!pathspec_matches(paths, down, recurse))
;
else if (S_ISREG(entry.mode))
hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@ -535,7 +544,8 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
die("unable to read tree (%s)",
sha1_to_hex(entry.sha1));
init_tree_desc(&sub, data, size);
- hit |= grep_tree(opt, paths, &sub, tree_name, down);
+ hit |= grep_tree(opt, paths, &sub, tree_name, down,
+ recurse);
free(data);
}
}
@@ -544,7 +554,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
}
static int grep_object(struct grep_opt *opt, const char **paths,
- struct object *obj, const char *name)
+ struct object *obj, const char *name, int recurse)
{
if (obj->type == OBJ_BLOB)
return grep_sha1(opt, obj->sha1, name, 0);
@@ -558,7 +568,7 @@ static int grep_object(struct grep_opt *opt, const char **paths,
if (!data)
die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
init_tree_desc(&tree, data, size);
- hit = grep_tree(opt, paths, &tree, name, "");
+ hit = grep_tree(opt, paths, &tree, name, "", recurse);
free(data);
return hit;
}
@@ -648,10 +658,32 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
return -1;
}
+static int directories_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ int *recurse = opt->value;
+
+ if (!arg)
+ return error("switch `d' requires a value");
+
+ if (!strcmp(arg, "recurse")) {
+ *recurse = 1;
+ return 0;
+ } else if (!strcmp(arg, "skip")) {
+ *recurse = 0;
+ return 0;
+ }
+
+ fprintf(stderr, "Invalid action `%s'.\n", arg);
+ fprintf(stderr, "Available actions are: recurse skip.\n");
+ return -1;
+}
+
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
int cached = 0;
+ int recurse = 1;
int external_grep_allowed = 1;
int seen_dashdash = 0;
struct grep_opt opt;
@@ -674,6 +706,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
+ OPT_CALLBACK('d', "directories", &recurse, "action",
+ "action to perform when input file is a directory",
+ directories_callback),
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
@@ -830,7 +865,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!list.nr) {
if (!cached)
setup_work_tree();
- return !grep_cache(&opt, paths, cached, external_grep_allowed);
+ return !grep_cache(&opt, paths, cached, external_grep_allowed,
+ recurse);
}
if (cached)
@@ -839,7 +875,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
for (i = 0; i < list.nr; i++) {
struct object *real_obj;
real_obj = deref_tag(list.objects[i].item, NULL, 0);
- if (grep_object(&opt, paths, real_obj, list.objects[i].name))
+ if (grep_object(&opt, paths, real_obj, list.objects[i].name,
+ recurse))
hit = 1;
}
free_grep_patterns(&opt);
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 7868af8..6d1faf4 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -22,7 +22,9 @@ test_expect_success setup '
echo zzz > z &&
mkdir t &&
echo test >t/t &&
- git add file w x y z t/t &&
+ mkdir t/a &&
+ echo aa aa aa aa >t/a/a &&
+ git add file w x y z t/t t/a/a &&
test_tick &&
git commit -m initial
'
@@ -123,6 +125,36 @@ do
! git grep -c test $H | grep /dev/null
'
+ test_expect_success "grep -d recurse $L" '
+ echo "${HC}t/t:1:test" >expected &&
+ git grep -d recurse -n -e test $H >actual &&
+ diff expected actual
+ '
+
+ test_expect_success "grep -d skip $L" '
+ : >expected &&
+ if git grep -d skip -e test $H >actual
+ then
+ echo should not have matched
+ cat actual
+ false
+ else
+ diff expected actual
+ fi
+ '
+
+ test_expect_success "grep -d skip $L -- t" '
+ : >expected &&
+ if git grep -d skip -e aa $H -- t >actual
+ then
+ echo should not have matched
+ cat actual
+ false
+ else
+ diff expected actual
+ fi
+ '
+
done
cat >expected <<EOF
--
1.6.3.3
next reply other threads:[~2009-07-09 19:21 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-07-09 19:20 Michał Kiedrowicz [this message]
2009-07-10 7:33 ` [PATCH/RFC] grep: Add --directories option René Scharfe
2009-07-10 8:11 ` Junio C Hamano
2009-07-10 16:48 ` Michał Kiedrowicz
2009-07-10 16:41 ` Michał Kiedrowicz
2009-07-10 8:02 ` Stephen Boyd
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1247167228-2466-1-git-send-email-michal.kiedrowicz@gmail.com \
--to=michal.kiedrowicz@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).