Merge branch 'fk/threaded-grep'
* fk/threaded-grep: Threaded grep grep: expose "status-only" feature via -q
This commit is contained in:
commit
b62cb17a65
413
builtin-grep.c
413
builtin-grep.c
@ -16,11 +16,274 @@
|
|||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
#include "thread-utils.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static char const * const grep_usage[] = {
|
static char const * const grep_usage[] = {
|
||||||
"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
|
"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int use_threads = 1;
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
#define THREADS 8
|
||||||
|
static pthread_t threads[THREADS];
|
||||||
|
|
||||||
|
static void *load_sha1(const unsigned char *sha1, unsigned long *size,
|
||||||
|
const char *name);
|
||||||
|
static void *load_file(const char *filename, size_t *sz);
|
||||||
|
|
||||||
|
enum work_type {WORK_SHA1, WORK_FILE};
|
||||||
|
|
||||||
|
/* We use one producer thread and THREADS consumer
|
||||||
|
* threads. The producer adds struct work_items to 'todo' and the
|
||||||
|
* consumers pick work items from the same array.
|
||||||
|
*/
|
||||||
|
struct work_item
|
||||||
|
{
|
||||||
|
enum work_type type;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* if type == WORK_SHA1, then 'identifier' is a SHA1,
|
||||||
|
* otherwise type == WORK_FILE, and 'identifier' is a NUL
|
||||||
|
* terminated filename.
|
||||||
|
*/
|
||||||
|
void *identifier;
|
||||||
|
char done;
|
||||||
|
struct strbuf out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* In the range [todo_done, todo_start) in 'todo' we have work_items
|
||||||
|
* that have been or are processed by a consumer thread. We haven't
|
||||||
|
* written the result for these to stdout yet.
|
||||||
|
*
|
||||||
|
* The work_items in [todo_start, todo_end) are waiting to be picked
|
||||||
|
* up by a consumer thread.
|
||||||
|
*
|
||||||
|
* The ranges are modulo TODO_SIZE.
|
||||||
|
*/
|
||||||
|
#define TODO_SIZE 128
|
||||||
|
static struct work_item todo[TODO_SIZE];
|
||||||
|
static int todo_start;
|
||||||
|
static int todo_end;
|
||||||
|
static int todo_done;
|
||||||
|
|
||||||
|
/* Has all work items been added? */
|
||||||
|
static int all_work_added;
|
||||||
|
|
||||||
|
/* This lock protects all the variables above. */
|
||||||
|
static pthread_mutex_t grep_mutex;
|
||||||
|
|
||||||
|
/* Used to serialize calls to read_sha1_file. */
|
||||||
|
static pthread_mutex_t read_sha1_mutex;
|
||||||
|
|
||||||
|
#define grep_lock() pthread_mutex_lock(&grep_mutex)
|
||||||
|
#define grep_unlock() pthread_mutex_unlock(&grep_mutex)
|
||||||
|
#define read_sha1_lock() pthread_mutex_lock(&read_sha1_mutex)
|
||||||
|
#define read_sha1_unlock() pthread_mutex_unlock(&read_sha1_mutex)
|
||||||
|
|
||||||
|
/* Signalled when a new work_item is added to todo. */
|
||||||
|
static pthread_cond_t cond_add;
|
||||||
|
|
||||||
|
/* Signalled when the result from one work_item is written to
|
||||||
|
* stdout.
|
||||||
|
*/
|
||||||
|
static pthread_cond_t cond_write;
|
||||||
|
|
||||||
|
/* Signalled when we are finished with everything. */
|
||||||
|
static pthread_cond_t cond_result;
|
||||||
|
|
||||||
|
static void add_work(enum work_type type, char *name, void *id)
|
||||||
|
{
|
||||||
|
grep_lock();
|
||||||
|
|
||||||
|
while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) {
|
||||||
|
pthread_cond_wait(&cond_write, &grep_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
todo[todo_end].type = type;
|
||||||
|
todo[todo_end].name = name;
|
||||||
|
todo[todo_end].identifier = id;
|
||||||
|
todo[todo_end].done = 0;
|
||||||
|
strbuf_reset(&todo[todo_end].out);
|
||||||
|
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
|
||||||
|
|
||||||
|
pthread_cond_signal(&cond_add);
|
||||||
|
grep_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct work_item *get_work(void)
|
||||||
|
{
|
||||||
|
struct work_item *ret;
|
||||||
|
|
||||||
|
grep_lock();
|
||||||
|
while (todo_start == todo_end && !all_work_added) {
|
||||||
|
pthread_cond_wait(&cond_add, &grep_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (todo_start == todo_end && all_work_added) {
|
||||||
|
ret = NULL;
|
||||||
|
} else {
|
||||||
|
ret = &todo[todo_start];
|
||||||
|
todo_start = (todo_start + 1) % ARRAY_SIZE(todo);
|
||||||
|
}
|
||||||
|
grep_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void grep_sha1_async(struct grep_opt *opt, char *name,
|
||||||
|
const unsigned char *sha1)
|
||||||
|
{
|
||||||
|
unsigned char *s;
|
||||||
|
s = xmalloc(20);
|
||||||
|
memcpy(s, sha1, 20);
|
||||||
|
add_work(WORK_SHA1, name, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void grep_file_async(struct grep_opt *opt, char *name,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
add_work(WORK_FILE, name, xstrdup(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void work_done(struct work_item *w)
|
||||||
|
{
|
||||||
|
int old_done;
|
||||||
|
|
||||||
|
grep_lock();
|
||||||
|
w->done = 1;
|
||||||
|
old_done = todo_done;
|
||||||
|
for(; todo[todo_done].done && todo_done != todo_start;
|
||||||
|
todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
|
||||||
|
w = &todo[todo_done];
|
||||||
|
write_or_die(1, w->out.buf, w->out.len);
|
||||||
|
free(w->name);
|
||||||
|
free(w->identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_done != todo_done)
|
||||||
|
pthread_cond_signal(&cond_write);
|
||||||
|
|
||||||
|
if (all_work_added && todo_done == todo_end)
|
||||||
|
pthread_cond_signal(&cond_result);
|
||||||
|
|
||||||
|
grep_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *run(void *arg)
|
||||||
|
{
|
||||||
|
int hit = 0;
|
||||||
|
struct grep_opt *opt = arg;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct work_item *w = get_work();
|
||||||
|
if (!w)
|
||||||
|
break;
|
||||||
|
|
||||||
|
opt->output_priv = w;
|
||||||
|
if (w->type == WORK_SHA1) {
|
||||||
|
unsigned long sz;
|
||||||
|
void* data = load_sha1(w->identifier, &sz, w->name);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
hit |= grep_buffer(opt, w->name, data, sz);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
} else if (w->type == WORK_FILE) {
|
||||||
|
size_t sz;
|
||||||
|
void* data = load_file(w->identifier, &sz);
|
||||||
|
if (data) {
|
||||||
|
hit |= grep_buffer(opt, w->name, data, sz);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
work_done(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*) (intptr_t) hit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strbuf_out(struct grep_opt *opt, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct work_item *w = opt->output_priv;
|
||||||
|
strbuf_add(&w->out, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_threads(struct grep_opt *opt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_mutex_init(&grep_mutex, NULL);
|
||||||
|
pthread_mutex_init(&read_sha1_mutex, NULL);
|
||||||
|
pthread_cond_init(&cond_add, NULL);
|
||||||
|
pthread_cond_init(&cond_write, NULL);
|
||||||
|
pthread_cond_init(&cond_result, NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(todo); i++) {
|
||||||
|
strbuf_init(&todo[i].out, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(threads); i++) {
|
||||||
|
int err;
|
||||||
|
struct grep_opt *o = grep_opt_dup(opt);
|
||||||
|
o->output = strbuf_out;
|
||||||
|
compile_grep_patterns(o);
|
||||||
|
err = pthread_create(&threads[i], NULL, run, o);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
die("grep: failed to create thread: %s",
|
||||||
|
strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wait_all(void)
|
||||||
|
{
|
||||||
|
int hit = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
grep_lock();
|
||||||
|
all_work_added = 1;
|
||||||
|
|
||||||
|
/* Wait until all work is done. */
|
||||||
|
while (todo_done != todo_end)
|
||||||
|
pthread_cond_wait(&cond_result, &grep_mutex);
|
||||||
|
|
||||||
|
/* Wake up all the consumer threads so they can see that there
|
||||||
|
* is no more work to do.
|
||||||
|
*/
|
||||||
|
pthread_cond_broadcast(&cond_add);
|
||||||
|
grep_unlock();
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(threads); i++) {
|
||||||
|
void *h;
|
||||||
|
pthread_join(threads[i], &h);
|
||||||
|
hit |= (int) (intptr_t) h;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&grep_mutex);
|
||||||
|
pthread_mutex_destroy(&read_sha1_mutex);
|
||||||
|
pthread_cond_destroy(&cond_add);
|
||||||
|
pthread_cond_destroy(&cond_write);
|
||||||
|
pthread_cond_destroy(&cond_result);
|
||||||
|
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
#else /* !NO_PTHREADS */
|
||||||
|
#define read_sha1_lock()
|
||||||
|
#define read_sha1_unlock()
|
||||||
|
|
||||||
|
static int wait_all(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int grep_config(const char *var, const char *value, void *cb)
|
static int grep_config(const char *var, const char *value, void *cb)
|
||||||
{
|
{
|
||||||
struct grep_opt *opt = cb;
|
struct grep_opt *opt = cb;
|
||||||
@ -144,37 +407,64 @@ static int pathspec_matches(const char **paths, const char *name, int max_depth)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
|
static void *load_sha1(const unsigned char *sha1, unsigned long *size,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
unsigned long size;
|
|
||||||
char *data;
|
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
int hit;
|
char *data;
|
||||||
struct strbuf pathbuf = STRBUF_INIT;
|
|
||||||
|
|
||||||
data = read_sha1_file(sha1, &type, &size);
|
read_sha1_lock();
|
||||||
if (!data) {
|
data = read_sha1_file(sha1, &type, size);
|
||||||
|
read_sha1_unlock();
|
||||||
|
|
||||||
|
if (!data)
|
||||||
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
|
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
|
||||||
return 0;
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
|
||||||
|
const char *filename, int tree_name_len)
|
||||||
|
{
|
||||||
|
struct strbuf pathbuf = STRBUF_INIT;
|
||||||
|
char *name;
|
||||||
|
|
||||||
if (opt->relative && opt->prefix_length) {
|
if (opt->relative && opt->prefix_length) {
|
||||||
quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
|
quote_path_relative(filename + tree_name_len, -1, &pathbuf,
|
||||||
strbuf_insert(&pathbuf, 0, name, tree_name_len);
|
opt->prefix);
|
||||||
name = pathbuf.buf;
|
strbuf_insert(&pathbuf, 0, filename, tree_name_len);
|
||||||
|
} else {
|
||||||
|
strbuf_addstr(&pathbuf, filename);
|
||||||
}
|
}
|
||||||
hit = grep_buffer(opt, name, data, size);
|
|
||||||
strbuf_release(&pathbuf);
|
name = strbuf_detach(&pathbuf, NULL);
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
if (use_threads) {
|
||||||
|
grep_sha1_async(opt, name, sha1);
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int hit;
|
||||||
|
unsigned long sz;
|
||||||
|
void *data = load_sha1(sha1, &sz, name);
|
||||||
|
if (!data)
|
||||||
|
hit = 0;
|
||||||
|
else
|
||||||
|
hit = grep_buffer(opt, name, data, sz);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
free(name);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int grep_file(struct grep_opt *opt, const char *filename)
|
static void *load_file(const char *filename, size_t *sz)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int i;
|
|
||||||
char *data;
|
char *data;
|
||||||
size_t sz;
|
int i;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
|
||||||
|
|
||||||
if (lstat(filename, &st) < 0) {
|
if (lstat(filename, &st) < 0) {
|
||||||
err_ret:
|
err_ret:
|
||||||
@ -184,25 +474,52 @@ static int grep_file(struct grep_opt *opt, const char *filename)
|
|||||||
}
|
}
|
||||||
if (!S_ISREG(st.st_mode))
|
if (!S_ISREG(st.st_mode))
|
||||||
return 0;
|
return 0;
|
||||||
sz = xsize_t(st.st_size);
|
*sz = xsize_t(st.st_size);
|
||||||
i = open(filename, O_RDONLY);
|
i = open(filename, O_RDONLY);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
goto err_ret;
|
goto err_ret;
|
||||||
data = xmalloc(sz + 1);
|
data = xmalloc(*sz + 1);
|
||||||
if (st.st_size != read_in_full(i, data, sz)) {
|
if (st.st_size != read_in_full(i, data, *sz)) {
|
||||||
error("'%s': short read %s", filename, strerror(errno));
|
error("'%s': short read %s", filename, strerror(errno));
|
||||||
close(i);
|
close(i);
|
||||||
free(data);
|
free(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
close(i);
|
close(i);
|
||||||
data[sz] = 0;
|
data[*sz] = 0;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grep_file(struct grep_opt *opt, const char *filename)
|
||||||
|
{
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
char *name;
|
||||||
|
|
||||||
if (opt->relative && opt->prefix_length)
|
if (opt->relative && opt->prefix_length)
|
||||||
filename = quote_path_relative(filename, -1, &buf, opt->prefix);
|
quote_path_relative(filename, -1, &buf, opt->prefix);
|
||||||
i = grep_buffer(opt, filename, data, sz);
|
else
|
||||||
strbuf_release(&buf);
|
strbuf_addstr(&buf, filename);
|
||||||
|
name = strbuf_detach(&buf, NULL);
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
if (use_threads) {
|
||||||
|
grep_file_async(opt, name, filename);
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int hit;
|
||||||
|
size_t sz;
|
||||||
|
void *data = load_file(filename, &sz);
|
||||||
|
if (!data)
|
||||||
|
hit = 0;
|
||||||
|
else
|
||||||
|
hit = grep_buffer(opt, name, data, sz);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
return i;
|
free(name);
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||||
@ -236,6 +553,8 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
|||||||
!strcmp(ce->name, active_cache[nr]->name));
|
!strcmp(ce->name, active_cache[nr]->name));
|
||||||
nr--; /* compensate for loop control */
|
nr--; /* compensate for loop control */
|
||||||
}
|
}
|
||||||
|
if (hit && opt->status_only)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
free_grep_patterns(opt);
|
free_grep_patterns(opt);
|
||||||
return hit;
|
return hit;
|
||||||
@ -285,7 +604,10 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
|
|||||||
void *data;
|
void *data;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
|
||||||
|
read_sha1_lock();
|
||||||
data = read_sha1_file(entry.sha1, &type, &size);
|
data = read_sha1_file(entry.sha1, &type, &size);
|
||||||
|
read_sha1_unlock();
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
die("unable to read tree (%s)",
|
die("unable to read tree (%s)",
|
||||||
sha1_to_hex(entry.sha1));
|
sha1_to_hex(entry.sha1));
|
||||||
@ -293,6 +615,8 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
|
|||||||
hit |= grep_tree(opt, paths, &sub, tree_name, down);
|
hit |= grep_tree(opt, paths, &sub, tree_name, down);
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
if (hit && opt->status_only)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
strbuf_release(&pathbuf);
|
strbuf_release(&pathbuf);
|
||||||
return hit;
|
return hit;
|
||||||
@ -329,8 +653,11 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
|
|||||||
setup_standard_excludes(&dir);
|
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++) {
|
||||||
hit |= grep_file(opt, dir.entries[i]->name);
|
hit |= grep_file(opt, dir.entries[i]->name);
|
||||||
|
if (hit && opt->status_only)
|
||||||
|
break;
|
||||||
|
}
|
||||||
free_grep_patterns(opt);
|
free_grep_patterns(opt);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
@ -505,6 +832,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
|
{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
|
||||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
|
||||||
close_callback },
|
close_callback },
|
||||||
|
OPT_BOOLEAN('q', "quick", &opt.status_only,
|
||||||
|
"indicate hit with exit status without output"),
|
||||||
OPT_BOOLEAN(0, "all-match", &opt.all_match,
|
OPT_BOOLEAN(0, "all-match", &opt.all_match,
|
||||||
"show only matches from files that match all patterns"),
|
"show only matches from files that match all patterns"),
|
||||||
OPT_GROUP(""),
|
OPT_GROUP(""),
|
||||||
@ -572,6 +901,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
opt.regflags |= REG_ICASE;
|
opt.regflags |= REG_ICASE;
|
||||||
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
||||||
die("cannot mix --fixed-strings and regexp");
|
die("cannot mix --fixed-strings and regexp");
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
if (online_cpus() == 1 || !grep_threads_ok(&opt))
|
||||||
|
use_threads = 0;
|
||||||
|
|
||||||
|
if (use_threads)
|
||||||
|
start_threads(&opt);
|
||||||
|
#else
|
||||||
|
use_threads = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
compile_grep_patterns(&opt);
|
compile_grep_patterns(&opt);
|
||||||
|
|
||||||
/* Check revs and then paths */
|
/* Check revs and then paths */
|
||||||
@ -609,17 +949,26 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!use_index) {
|
if (!use_index) {
|
||||||
|
int hit;
|
||||||
if (cached)
|
if (cached)
|
||||||
die("--cached cannot be used with --no-index.");
|
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 cannot be used with revs.");
|
||||||
return !grep_directory(&opt, paths);
|
hit = grep_directory(&opt, paths);
|
||||||
|
if (use_threads)
|
||||||
|
hit |= wait_all();
|
||||||
|
return !hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list.nr) {
|
if (!list.nr) {
|
||||||
|
int hit;
|
||||||
if (!cached)
|
if (!cached)
|
||||||
setup_work_tree();
|
setup_work_tree();
|
||||||
return !grep_cache(&opt, paths, cached);
|
|
||||||
|
hit = grep_cache(&opt, paths, cached);
|
||||||
|
if (use_threads)
|
||||||
|
hit |= wait_all();
|
||||||
|
return !hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached)
|
if (cached)
|
||||||
@ -628,9 +977,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
for (i = 0; i < list.nr; i++) {
|
for (i = 0; i < list.nr; i++) {
|
||||||
struct object *real_obj;
|
struct object *real_obj;
|
||||||
real_obj = deref_tag(list.objects[i].item, NULL, 0);
|
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)) {
|
||||||
hit = 1;
|
hit = 1;
|
||||||
|
if (opt.status_only)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_threads)
|
||||||
|
hit |= wait_all();
|
||||||
free_grep_patterns(&opt);
|
free_grep_patterns(&opt);
|
||||||
return !hit;
|
return !hit;
|
||||||
}
|
}
|
||||||
|
104
grep.c
104
grep.c
@ -29,6 +29,28 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
|
|||||||
p->next = NULL;
|
p->next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
|
||||||
|
{
|
||||||
|
struct grep_pat *pat;
|
||||||
|
struct grep_opt *ret = xmalloc(sizeof(struct grep_opt));
|
||||||
|
*ret = *opt;
|
||||||
|
|
||||||
|
ret->pattern_list = NULL;
|
||||||
|
ret->pattern_tail = &ret->pattern_list;
|
||||||
|
|
||||||
|
for(pat = opt->pattern_list; pat != NULL; pat = pat->next)
|
||||||
|
{
|
||||||
|
if(pat->token == GREP_PATTERN_HEAD)
|
||||||
|
append_header_grep_pattern(ret, pat->field,
|
||||||
|
pat->pattern);
|
||||||
|
else
|
||||||
|
append_grep_pattern(ret, pat->pattern, pat->origin,
|
||||||
|
pat->no, pat->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -253,7 +275,8 @@ static int word_char(char ch)
|
|||||||
|
|
||||||
static void show_name(struct grep_opt *opt, const char *name)
|
static void show_name(struct grep_opt *opt, const char *name)
|
||||||
{
|
{
|
||||||
printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
|
opt->output(opt, name, strlen(name));
|
||||||
|
opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -490,24 +513,32 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
|||||||
const char *name, unsigned lno, char sign)
|
const char *name, unsigned lno, char sign)
|
||||||
{
|
{
|
||||||
int rest = eol - bol;
|
int rest = eol - bol;
|
||||||
|
char sign_str[1];
|
||||||
|
|
||||||
|
sign_str[0] = sign;
|
||||||
if (opt->pre_context || opt->post_context) {
|
if (opt->pre_context || opt->post_context) {
|
||||||
if (opt->last_shown == 0) {
|
if (opt->last_shown == 0) {
|
||||||
if (opt->show_hunk_mark)
|
if (opt->show_hunk_mark)
|
||||||
fputs("--\n", stdout);
|
opt->output(opt, "--\n", 3);
|
||||||
else
|
else
|
||||||
opt->show_hunk_mark = 1;
|
opt->show_hunk_mark = 1;
|
||||||
} else if (lno > opt->last_shown + 1)
|
} else if (lno > opt->last_shown + 1)
|
||||||
fputs("--\n", stdout);
|
opt->output(opt, "--\n", 3);
|
||||||
}
|
}
|
||||||
opt->last_shown = lno;
|
opt->last_shown = lno;
|
||||||
|
|
||||||
if (opt->null_following_name)
|
if (opt->null_following_name)
|
||||||
sign = '\0';
|
sign_str[0] = '\0';
|
||||||
if (opt->pathname)
|
if (opt->pathname) {
|
||||||
printf("%s%c", name, sign);
|
opt->output(opt, name, strlen(name));
|
||||||
if (opt->linenum)
|
opt->output(opt, sign_str, 1);
|
||||||
printf("%d%c", lno, sign);
|
}
|
||||||
|
if (opt->linenum) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%d", lno);
|
||||||
|
opt->output(opt, buf, strlen(buf));
|
||||||
|
opt->output(opt, sign_str, 1);
|
||||||
|
}
|
||||||
if (opt->color) {
|
if (opt->color) {
|
||||||
regmatch_t match;
|
regmatch_t match;
|
||||||
enum grep_context ctx = GREP_CONTEXT_BODY;
|
enum grep_context ctx = GREP_CONTEXT_BODY;
|
||||||
@ -518,18 +549,22 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
|||||||
while (next_match(opt, bol, eol, ctx, &match, eflags)) {
|
while (next_match(opt, bol, eol, ctx, &match, eflags)) {
|
||||||
if (match.rm_so == match.rm_eo)
|
if (match.rm_so == match.rm_eo)
|
||||||
break;
|
break;
|
||||||
printf("%.*s%s%.*s%s",
|
|
||||||
(int)match.rm_so, bol,
|
opt->output(opt, bol, match.rm_so);
|
||||||
opt->color_match,
|
opt->output(opt, opt->color_match,
|
||||||
(int)(match.rm_eo - match.rm_so), bol + match.rm_so,
|
strlen(opt->color_match));
|
||||||
GIT_COLOR_RESET);
|
opt->output(opt, bol + match.rm_so,
|
||||||
|
(int)(match.rm_eo - match.rm_so));
|
||||||
|
opt->output(opt, GIT_COLOR_RESET,
|
||||||
|
strlen(GIT_COLOR_RESET));
|
||||||
bol += match.rm_eo;
|
bol += match.rm_eo;
|
||||||
rest -= match.rm_eo;
|
rest -= match.rm_eo;
|
||||||
eflags = REG_NOTBOL;
|
eflags = REG_NOTBOL;
|
||||||
}
|
}
|
||||||
*eol = ch;
|
*eol = ch;
|
||||||
}
|
}
|
||||||
printf("%.*s\n", rest, bol);
|
opt->output(opt, bol, rest);
|
||||||
|
opt->output(opt, "\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
|
static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
|
||||||
@ -674,6 +709,32 @@ static int look_ahead(struct grep_opt *opt,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int grep_threads_ok(const struct grep_opt *opt)
|
||||||
|
{
|
||||||
|
/* If this condition is true, then we may use the attribute
|
||||||
|
* machinery in grep_buffer_1. The attribute code is not
|
||||||
|
* thread safe, so we disable the use of threads.
|
||||||
|
*/
|
||||||
|
if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
|
||||||
|
!opt->name_only)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If we are showing hunk marks, we should not do it for the
|
||||||
|
* first match. The synchronization problem we get for this
|
||||||
|
* constraint is not yet solved, so we disable threading in
|
||||||
|
* this case.
|
||||||
|
*/
|
||||||
|
if (opt->pre_context || opt->post_context)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void std_output(struct grep_opt *opt, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
fwrite(buf, size, 1, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
char *buf, unsigned long size, int collect_hits)
|
char *buf, unsigned long size, int collect_hits)
|
||||||
{
|
{
|
||||||
@ -689,6 +750,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||||||
|
|
||||||
opt->last_shown = 0;
|
opt->last_shown = 0;
|
||||||
|
|
||||||
|
if (!opt->output)
|
||||||
|
opt->output = std_output;
|
||||||
|
|
||||||
if (buffer_is_binary(buf, size)) {
|
if (buffer_is_binary(buf, size)) {
|
||||||
switch (opt->binary) {
|
switch (opt->binary) {
|
||||||
case GREP_BINARY_DEFAULT:
|
case GREP_BINARY_DEFAULT:
|
||||||
@ -761,7 +825,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||||||
if (opt->status_only)
|
if (opt->status_only)
|
||||||
return 1;
|
return 1;
|
||||||
if (binary_match_only) {
|
if (binary_match_only) {
|
||||||
printf("Binary file %s matches\n", name);
|
opt->output(opt, "Binary file ", 12);
|
||||||
|
opt->output(opt, name, strlen(name));
|
||||||
|
opt->output(opt, " matches\n", 9);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (opt->name_only) {
|
if (opt->name_only) {
|
||||||
@ -817,9 +883,13 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||||||
* which feels mostly useless but sometimes useful. Maybe
|
* which feels mostly useless but sometimes useful. Maybe
|
||||||
* make it another option? For now suppress them.
|
* make it another option? For now suppress them.
|
||||||
*/
|
*/
|
||||||
if (opt->count && count)
|
if (opt->count && count) {
|
||||||
printf("%s%c%u\n", name,
|
char buf[32];
|
||||||
|
opt->output(opt, name, strlen(name));
|
||||||
|
snprintf(buf, sizeof(buf), "%c%u\n",
|
||||||
opt->null_following_name ? '\0' : ':', count);
|
opt->null_following_name ? '\0' : ':', count);
|
||||||
|
opt->output(opt, buf, strlen(buf));
|
||||||
|
}
|
||||||
return !!last_hit;
|
return !!last_hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
grep.h
6
grep.h
@ -91,6 +91,9 @@ struct grep_opt {
|
|||||||
unsigned last_shown;
|
unsigned last_shown;
|
||||||
int show_hunk_mark;
|
int show_hunk_mark;
|
||||||
void *priv;
|
void *priv;
|
||||||
|
|
||||||
|
void (*output)(struct grep_opt *opt, const void *data, size_t size);
|
||||||
|
void *output_priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
|
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
|
||||||
@ -99,4 +102,7 @@ extern void compile_grep_patterns(struct grep_opt *opt);
|
|||||||
extern void free_grep_patterns(struct grep_opt *opt);
|
extern void free_grep_patterns(struct grep_opt *opt);
|
||||||
extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
|
extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
|
||||||
|
|
||||||
|
extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
|
||||||
|
extern int grep_threads_ok(const struct grep_opt *opt);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user