зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/grep-binary-attribute'
* jk/grep-binary-attribute: grep: pre-load userdiff drivers when threaded grep: load file data after checking binary-ness grep: respect diff attributes for binary-ness grep: cache userdiff_driver in grep_source grep: drop grep_buffer's "name" parameter convert git-grep to use grep_source interface grep: refactor the concept of "grep source" into an object grep: move sha1-reading mutex into low-level code grep: make locking flag global
This commit is contained in:
Коммит
10439fc0ef
177
builtin/grep.c
177
builtin/grep.c
|
@ -29,25 +29,12 @@ static int use_threads = 1;
|
||||||
#define THREADS 8
|
#define THREADS 8
|
||||||
static pthread_t threads[THREADS];
|
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
|
/* We use one producer thread and THREADS consumer
|
||||||
* threads. The producer adds struct work_items to 'todo' and the
|
* threads. The producer adds struct work_items to 'todo' and the
|
||||||
* consumers pick work items from the same array.
|
* consumers pick work items from the same array.
|
||||||
*/
|
*/
|
||||||
struct work_item {
|
struct work_item {
|
||||||
enum work_type type;
|
struct grep_source source;
|
||||||
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;
|
char done;
|
||||||
struct strbuf out;
|
struct strbuf out;
|
||||||
};
|
};
|
||||||
|
@ -85,21 +72,6 @@ static inline void grep_unlock(void)
|
||||||
pthread_mutex_unlock(&grep_mutex);
|
pthread_mutex_unlock(&grep_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used to serialize calls to read_sha1_file. */
|
|
||||||
static pthread_mutex_t read_sha1_mutex;
|
|
||||||
|
|
||||||
static inline void read_sha1_lock(void)
|
|
||||||
{
|
|
||||||
if (use_threads)
|
|
||||||
pthread_mutex_lock(&read_sha1_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void read_sha1_unlock(void)
|
|
||||||
{
|
|
||||||
if (use_threads)
|
|
||||||
pthread_mutex_unlock(&read_sha1_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signalled when a new work_item is added to todo. */
|
/* Signalled when a new work_item is added to todo. */
|
||||||
static pthread_cond_t cond_add;
|
static pthread_cond_t cond_add;
|
||||||
|
|
||||||
|
@ -113,7 +85,8 @@ static pthread_cond_t cond_result;
|
||||||
|
|
||||||
static int skip_first_line;
|
static int skip_first_line;
|
||||||
|
|
||||||
static void add_work(enum work_type type, char *name, void *id)
|
static void add_work(struct grep_opt *opt, enum grep_source_type type,
|
||||||
|
const char *name, const void *id)
|
||||||
{
|
{
|
||||||
grep_lock();
|
grep_lock();
|
||||||
|
|
||||||
|
@ -121,9 +94,9 @@ static void add_work(enum work_type type, char *name, void *id)
|
||||||
pthread_cond_wait(&cond_write, &grep_mutex);
|
pthread_cond_wait(&cond_write, &grep_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo[todo_end].type = type;
|
grep_source_init(&todo[todo_end].source, type, name, id);
|
||||||
todo[todo_end].name = name;
|
if (opt->binary != GREP_BINARY_TEXT)
|
||||||
todo[todo_end].identifier = id;
|
grep_source_load_driver(&todo[todo_end].source);
|
||||||
todo[todo_end].done = 0;
|
todo[todo_end].done = 0;
|
||||||
strbuf_reset(&todo[todo_end].out);
|
strbuf_reset(&todo[todo_end].out);
|
||||||
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
|
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
|
||||||
|
@ -151,21 +124,6 @@ static struct work_item *get_work(void)
|
||||||
return ret;
|
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)
|
static void work_done(struct work_item *w)
|
||||||
{
|
{
|
||||||
int old_done;
|
int old_done;
|
||||||
|
@ -192,8 +150,7 @@ static void work_done(struct work_item *w)
|
||||||
|
|
||||||
write_or_die(1, p, len);
|
write_or_die(1, p, len);
|
||||||
}
|
}
|
||||||
free(w->name);
|
grep_source_clear(&w->source);
|
||||||
free(w->identifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_done != todo_done)
|
if (old_done != todo_done)
|
||||||
|
@ -216,25 +173,8 @@ static void *run(void *arg)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
opt->output_priv = w;
|
opt->output_priv = w;
|
||||||
if (w->type == WORK_SHA1) {
|
hit |= grep_source(opt, &w->source);
|
||||||
unsigned long sz;
|
grep_source_clear_data(&w->source);
|
||||||
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);
|
work_done(w);
|
||||||
}
|
}
|
||||||
free_grep_patterns(arg);
|
free_grep_patterns(arg);
|
||||||
|
@ -254,11 +194,12 @@ static void start_threads(struct grep_opt *opt)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pthread_mutex_init(&grep_mutex, NULL);
|
pthread_mutex_init(&grep_mutex, NULL);
|
||||||
pthread_mutex_init(&read_sha1_mutex, NULL);
|
pthread_mutex_init(&grep_read_mutex, NULL);
|
||||||
pthread_mutex_init(&grep_attr_mutex, NULL);
|
pthread_mutex_init(&grep_attr_mutex, NULL);
|
||||||
pthread_cond_init(&cond_add, NULL);
|
pthread_cond_init(&cond_add, NULL);
|
||||||
pthread_cond_init(&cond_write, NULL);
|
pthread_cond_init(&cond_write, NULL);
|
||||||
pthread_cond_init(&cond_result, NULL);
|
pthread_cond_init(&cond_result, NULL);
|
||||||
|
grep_use_locks = 1;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(todo); i++) {
|
for (i = 0; i < ARRAY_SIZE(todo); i++) {
|
||||||
strbuf_init(&todo[i].out, 0);
|
strbuf_init(&todo[i].out, 0);
|
||||||
|
@ -302,17 +243,16 @@ static int wait_all(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_destroy(&grep_mutex);
|
pthread_mutex_destroy(&grep_mutex);
|
||||||
pthread_mutex_destroy(&read_sha1_mutex);
|
pthread_mutex_destroy(&grep_read_mutex);
|
||||||
pthread_mutex_destroy(&grep_attr_mutex);
|
pthread_mutex_destroy(&grep_attr_mutex);
|
||||||
pthread_cond_destroy(&cond_add);
|
pthread_cond_destroy(&cond_add);
|
||||||
pthread_cond_destroy(&cond_write);
|
pthread_cond_destroy(&cond_write);
|
||||||
pthread_cond_destroy(&cond_result);
|
pthread_cond_destroy(&cond_result);
|
||||||
|
grep_use_locks = 0;
|
||||||
|
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
#else /* !NO_PTHREADS */
|
#else /* !NO_PTHREADS */
|
||||||
#define read_sha1_lock()
|
|
||||||
#define read_sha1_unlock()
|
|
||||||
|
|
||||||
static int wait_all(void)
|
static int wait_all(void)
|
||||||
{
|
{
|
||||||
|
@ -371,21 +311,9 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type
|
||||||
{
|
{
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
read_sha1_lock();
|
grep_read_lock();
|
||||||
data = read_sha1_file(sha1, type, size);
|
data = read_sha1_file(sha1, type, size);
|
||||||
read_sha1_unlock();
|
grep_read_unlock();
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *load_sha1(const unsigned char *sha1, unsigned long *size,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
enum object_type type;
|
|
||||||
void *data = lock_and_read_sha1_file(sha1, &type, size);
|
|
||||||
|
|
||||||
if (!data)
|
|
||||||
error(_("'%s': unable to read %s"), name, sha1_to_hex(sha1));
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +321,6 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
|
||||||
const char *filename, int tree_name_len)
|
const char *filename, int tree_name_len)
|
||||||
{
|
{
|
||||||
struct strbuf pathbuf = STRBUF_INIT;
|
struct strbuf pathbuf = STRBUF_INIT;
|
||||||
char *name;
|
|
||||||
|
|
||||||
if (opt->relative && opt->prefix_length) {
|
if (opt->relative && opt->prefix_length) {
|
||||||
quote_path_relative(filename + tree_name_len, -1, &pathbuf,
|
quote_path_relative(filename + tree_name_len, -1, &pathbuf,
|
||||||
|
@ -403,87 +330,51 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
|
||||||
strbuf_addstr(&pathbuf, filename);
|
strbuf_addstr(&pathbuf, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
name = strbuf_detach(&pathbuf, NULL);
|
|
||||||
|
|
||||||
#ifndef NO_PTHREADS
|
#ifndef NO_PTHREADS
|
||||||
if (use_threads) {
|
if (use_threads) {
|
||||||
grep_sha1_async(opt, name, sha1);
|
add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
|
||||||
|
strbuf_release(&pathbuf);
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
struct grep_source gs;
|
||||||
int hit;
|
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);
|
grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
|
||||||
free(name);
|
strbuf_release(&pathbuf);
|
||||||
|
hit = grep_source(opt, &gs);
|
||||||
|
|
||||||
|
grep_source_clear(&gs);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *load_file(const char *filename, size_t *sz)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
char *data;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (lstat(filename, &st) < 0) {
|
|
||||||
err_ret:
|
|
||||||
if (errno != ENOENT)
|
|
||||||
error(_("'%s': %s"), filename, strerror(errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!S_ISREG(st.st_mode))
|
|
||||||
return NULL;
|
|
||||||
*sz = xsize_t(st.st_size);
|
|
||||||
i = open(filename, O_RDONLY);
|
|
||||||
if (i < 0)
|
|
||||||
goto err_ret;
|
|
||||||
data = xmalloc(*sz + 1);
|
|
||||||
if (st.st_size != read_in_full(i, data, *sz)) {
|
|
||||||
error(_("'%s': short read %s"), filename, strerror(errno));
|
|
||||||
close(i);
|
|
||||||
free(data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
close(i);
|
|
||||||
data[*sz] = 0;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grep_file(struct grep_opt *opt, const char *filename)
|
static int grep_file(struct grep_opt *opt, const char *filename)
|
||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
char *name;
|
|
||||||
|
|
||||||
if (opt->relative && opt->prefix_length)
|
if (opt->relative && opt->prefix_length)
|
||||||
quote_path_relative(filename, -1, &buf, opt->prefix);
|
quote_path_relative(filename, -1, &buf, opt->prefix);
|
||||||
else
|
else
|
||||||
strbuf_addstr(&buf, filename);
|
strbuf_addstr(&buf, filename);
|
||||||
name = strbuf_detach(&buf, NULL);
|
|
||||||
|
|
||||||
#ifndef NO_PTHREADS
|
#ifndef NO_PTHREADS
|
||||||
if (use_threads) {
|
if (use_threads) {
|
||||||
grep_file_async(opt, name, filename);
|
add_work(opt, GREP_SOURCE_FILE, buf.buf, filename);
|
||||||
|
strbuf_release(&buf);
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
struct grep_source gs;
|
||||||
int hit;
|
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);
|
grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename);
|
||||||
free(name);
|
strbuf_release(&buf);
|
||||||
|
hit = grep_source(opt, &gs);
|
||||||
|
|
||||||
|
grep_source_clear(&gs);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,10 +503,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
struct strbuf base;
|
struct strbuf base;
|
||||||
int hit, len;
|
int hit, len;
|
||||||
|
|
||||||
read_sha1_lock();
|
grep_read_lock();
|
||||||
data = read_object_with_reference(obj->sha1, tree_type,
|
data = read_object_with_reference(obj->sha1, tree_type,
|
||||||
&size, NULL);
|
&size, NULL);
|
||||||
read_sha1_unlock();
|
grep_read_unlock();
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
|
die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
|
||||||
|
@ -1027,8 +918,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||||
use_threads = 0;
|
use_threads = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
opt.use_threads = use_threads;
|
|
||||||
|
|
||||||
#ifndef NO_PTHREADS
|
#ifndef NO_PTHREADS
|
||||||
if (use_threads) {
|
if (use_threads) {
|
||||||
if (!(opt.name_only || opt.unmatch_name_only || opt.count)
|
if (!(opt.name_only || opt.unmatch_name_only || opt.count)
|
||||||
|
|
250
grep.c
250
grep.c
|
@ -807,38 +807,43 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_PTHREADS
|
#ifndef NO_PTHREADS
|
||||||
|
int grep_use_locks;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This lock protects access to the gitattributes machinery, which is
|
* This lock protects access to the gitattributes machinery, which is
|
||||||
* not thread-safe.
|
* not thread-safe.
|
||||||
*/
|
*/
|
||||||
pthread_mutex_t grep_attr_mutex;
|
pthread_mutex_t grep_attr_mutex;
|
||||||
|
|
||||||
static inline void grep_attr_lock(struct grep_opt *opt)
|
static inline void grep_attr_lock(void)
|
||||||
{
|
{
|
||||||
if (opt->use_threads)
|
if (grep_use_locks)
|
||||||
pthread_mutex_lock(&grep_attr_mutex);
|
pthread_mutex_lock(&grep_attr_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void grep_attr_unlock(struct grep_opt *opt)
|
static inline void grep_attr_unlock(void)
|
||||||
{
|
{
|
||||||
if (opt->use_threads)
|
if (grep_use_locks)
|
||||||
pthread_mutex_unlock(&grep_attr_mutex);
|
pthread_mutex_unlock(&grep_attr_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as git_attr_mutex, but protecting the thread-unsafe object db access.
|
||||||
|
*/
|
||||||
|
pthread_mutex_t grep_read_mutex;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define grep_attr_lock(opt)
|
#define grep_attr_lock()
|
||||||
#define grep_attr_unlock(opt)
|
#define grep_attr_unlock()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int match_funcname(struct grep_opt *opt, const char *name, char *bol, char *eol)
|
static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol)
|
||||||
{
|
{
|
||||||
xdemitconf_t *xecfg = opt->priv;
|
xdemitconf_t *xecfg = opt->priv;
|
||||||
if (xecfg && !xecfg->find_func) {
|
if (xecfg && !xecfg->find_func) {
|
||||||
struct userdiff_driver *drv;
|
grep_source_load_driver(gs);
|
||||||
grep_attr_lock(opt);
|
if (gs->driver->funcname.pattern) {
|
||||||
drv = userdiff_find_by_path(name);
|
const struct userdiff_funcname *pe = &gs->driver->funcname;
|
||||||
grep_attr_unlock(opt);
|
|
||||||
if (drv && drv->funcname.pattern) {
|
|
||||||
const struct userdiff_funcname *pe = &drv->funcname;
|
|
||||||
xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
|
xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
|
||||||
} else {
|
} else {
|
||||||
xecfg = opt->priv = NULL;
|
xecfg = opt->priv = NULL;
|
||||||
|
@ -858,33 +863,33 @@ static int match_funcname(struct grep_opt *opt, const char *name, char *bol, cha
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_funcname_line(struct grep_opt *opt, const char *name,
|
static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
|
||||||
char *buf, char *bol, unsigned lno)
|
char *bol, unsigned lno)
|
||||||
{
|
{
|
||||||
while (bol > buf) {
|
while (bol > gs->buf) {
|
||||||
char *eol = --bol;
|
char *eol = --bol;
|
||||||
|
|
||||||
while (bol > buf && bol[-1] != '\n')
|
while (bol > gs->buf && bol[-1] != '\n')
|
||||||
bol--;
|
bol--;
|
||||||
lno--;
|
lno--;
|
||||||
|
|
||||||
if (lno <= opt->last_shown)
|
if (lno <= opt->last_shown)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (match_funcname(opt, name, bol, eol)) {
|
if (match_funcname(opt, gs, bol, eol)) {
|
||||||
show_line(opt, bol, eol, name, lno, '=');
|
show_line(opt, bol, eol, gs->name, lno, '=');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
|
||||||
char *bol, char *end, unsigned lno)
|
char *bol, char *end, unsigned lno)
|
||||||
{
|
{
|
||||||
unsigned cur = lno, from = 1, funcname_lno = 0;
|
unsigned cur = lno, from = 1, funcname_lno = 0;
|
||||||
int funcname_needed = !!opt->funcname;
|
int funcname_needed = !!opt->funcname;
|
||||||
|
|
||||||
if (opt->funcbody && !match_funcname(opt, name, bol, end))
|
if (opt->funcbody && !match_funcname(opt, gs, bol, end))
|
||||||
funcname_needed = 2;
|
funcname_needed = 2;
|
||||||
|
|
||||||
if (opt->pre_context < lno)
|
if (opt->pre_context < lno)
|
||||||
|
@ -893,14 +898,14 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
||||||
from = opt->last_shown + 1;
|
from = opt->last_shown + 1;
|
||||||
|
|
||||||
/* Rewind. */
|
/* Rewind. */
|
||||||
while (bol > buf &&
|
while (bol > gs->buf &&
|
||||||
cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
|
cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
|
||||||
char *eol = --bol;
|
char *eol = --bol;
|
||||||
|
|
||||||
while (bol > buf && bol[-1] != '\n')
|
while (bol > gs->buf && bol[-1] != '\n')
|
||||||
bol--;
|
bol--;
|
||||||
cur--;
|
cur--;
|
||||||
if (funcname_needed && match_funcname(opt, name, bol, eol)) {
|
if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
|
||||||
funcname_lno = cur;
|
funcname_lno = cur;
|
||||||
funcname_needed = 0;
|
funcname_needed = 0;
|
||||||
}
|
}
|
||||||
|
@ -908,7 +913,7 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
||||||
|
|
||||||
/* We need to look even further back to find a function signature. */
|
/* We need to look even further back to find a function signature. */
|
||||||
if (opt->funcname && funcname_needed)
|
if (opt->funcname && funcname_needed)
|
||||||
show_funcname_line(opt, name, buf, bol, cur);
|
show_funcname_line(opt, gs, bol, cur);
|
||||||
|
|
||||||
/* Back forward. */
|
/* Back forward. */
|
||||||
while (cur < lno) {
|
while (cur < lno) {
|
||||||
|
@ -916,7 +921,7 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
||||||
|
|
||||||
while (*eol != '\n')
|
while (*eol != '\n')
|
||||||
eol++;
|
eol++;
|
||||||
show_line(opt, bol, eol, name, cur, sign);
|
show_line(opt, bol, eol, gs->name, cur, sign);
|
||||||
bol = eol + 1;
|
bol = eol + 1;
|
||||||
cur++;
|
cur++;
|
||||||
}
|
}
|
||||||
|
@ -983,11 +988,10 @@ static void std_output(struct grep_opt *opt, const void *buf, size_t size)
|
||||||
fwrite(buf, size, 1, stdout);
|
fwrite(buf, size, 1, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
|
||||||
char *buf, unsigned long size, int collect_hits)
|
|
||||||
{
|
{
|
||||||
char *bol = buf;
|
char *bol;
|
||||||
unsigned long left = size;
|
unsigned long left;
|
||||||
unsigned lno = 1;
|
unsigned lno = 1;
|
||||||
unsigned last_hit = 0;
|
unsigned last_hit = 0;
|
||||||
int binary_match_only = 0;
|
int binary_match_only = 0;
|
||||||
|
@ -1017,11 +1021,11 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
|
|
||||||
switch (opt->binary) {
|
switch (opt->binary) {
|
||||||
case GREP_BINARY_DEFAULT:
|
case GREP_BINARY_DEFAULT:
|
||||||
if (buffer_is_binary(buf, size))
|
if (grep_source_is_binary(gs))
|
||||||
binary_match_only = 1;
|
binary_match_only = 1;
|
||||||
break;
|
break;
|
||||||
case GREP_BINARY_NOMATCH:
|
case GREP_BINARY_NOMATCH:
|
||||||
if (buffer_is_binary(buf, size))
|
if (grep_source_is_binary(gs))
|
||||||
return 0; /* Assume unmatch */
|
return 0; /* Assume unmatch */
|
||||||
break;
|
break;
|
||||||
case GREP_BINARY_TEXT:
|
case GREP_BINARY_TEXT:
|
||||||
|
@ -1035,6 +1039,11 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
|
|
||||||
try_lookahead = should_lookahead(opt);
|
try_lookahead = should_lookahead(opt);
|
||||||
|
|
||||||
|
if (grep_source_load(gs) < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bol = gs->buf;
|
||||||
|
left = gs->size;
|
||||||
while (left) {
|
while (left) {
|
||||||
char *eol, ch;
|
char *eol, ch;
|
||||||
int hit;
|
int hit;
|
||||||
|
@ -1083,14 +1092,14 @@ 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 (opt->name_only) {
|
if (opt->name_only) {
|
||||||
show_name(opt, name);
|
show_name(opt, gs->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (opt->count)
|
if (opt->count)
|
||||||
goto next_line;
|
goto next_line;
|
||||||
if (binary_match_only) {
|
if (binary_match_only) {
|
||||||
opt->output(opt, "Binary file ", 12);
|
opt->output(opt, "Binary file ", 12);
|
||||||
output_color(opt, name, strlen(name),
|
output_color(opt, gs->name, strlen(gs->name),
|
||||||
opt->color_filename);
|
opt->color_filename);
|
||||||
opt->output(opt, " matches\n", 9);
|
opt->output(opt, " matches\n", 9);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1099,23 +1108,23 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
* pre-context lines, we would need to show them.
|
* pre-context lines, we would need to show them.
|
||||||
*/
|
*/
|
||||||
if (opt->pre_context || opt->funcbody)
|
if (opt->pre_context || opt->funcbody)
|
||||||
show_pre_context(opt, name, buf, bol, eol, lno);
|
show_pre_context(opt, gs, bol, eol, lno);
|
||||||
else if (opt->funcname)
|
else if (opt->funcname)
|
||||||
show_funcname_line(opt, name, buf, bol, lno);
|
show_funcname_line(opt, gs, bol, lno);
|
||||||
show_line(opt, bol, eol, name, lno, ':');
|
show_line(opt, bol, eol, gs->name, lno, ':');
|
||||||
last_hit = lno;
|
last_hit = lno;
|
||||||
if (opt->funcbody)
|
if (opt->funcbody)
|
||||||
show_function = 1;
|
show_function = 1;
|
||||||
goto next_line;
|
goto next_line;
|
||||||
}
|
}
|
||||||
if (show_function && match_funcname(opt, name, bol, eol))
|
if (show_function && match_funcname(opt, gs, bol, eol))
|
||||||
show_function = 0;
|
show_function = 0;
|
||||||
if (show_function ||
|
if (show_function ||
|
||||||
(last_hit && lno <= last_hit + opt->post_context)) {
|
(last_hit && lno <= last_hit + opt->post_context)) {
|
||||||
/* If the last hit is within the post context,
|
/* If the last hit is within the post context,
|
||||||
* we need to show this line.
|
* we need to show this line.
|
||||||
*/
|
*/
|
||||||
show_line(opt, bol, eol, name, lno, '-');
|
show_line(opt, bol, eol, gs->name, lno, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
next_line:
|
next_line:
|
||||||
|
@ -1133,7 +1142,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
return 0;
|
return 0;
|
||||||
if (opt->unmatch_name_only) {
|
if (opt->unmatch_name_only) {
|
||||||
/* We did not see any hit, so we want to show this */
|
/* We did not see any hit, so we want to show this */
|
||||||
show_name(opt, name);
|
show_name(opt, gs->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,7 +1156,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||||
*/
|
*/
|
||||||
if (opt->count && count) {
|
if (opt->count && count) {
|
||||||
char buf[32];
|
char buf[32];
|
||||||
output_color(opt, name, strlen(name), opt->color_filename);
|
output_color(opt, gs->name, strlen(gs->name), opt->color_filename);
|
||||||
output_sep(opt, ':');
|
output_sep(opt, ':');
|
||||||
snprintf(buf, sizeof(buf), "%u\n", count);
|
snprintf(buf, sizeof(buf), "%u\n", count);
|
||||||
opt->output(opt, buf, strlen(buf));
|
opt->output(opt, buf, strlen(buf));
|
||||||
|
@ -1182,23 +1191,174 @@ static int chk_hit_marker(struct grep_expr *x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
|
int grep_source(struct grep_opt *opt, struct grep_source *gs)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* we do not have to do the two-pass grep when we do not check
|
* we do not have to do the two-pass grep when we do not check
|
||||||
* buffer-wide "all-match".
|
* buffer-wide "all-match".
|
||||||
*/
|
*/
|
||||||
if (!opt->all_match)
|
if (!opt->all_match)
|
||||||
return grep_buffer_1(opt, name, buf, size, 0);
|
return grep_source_1(opt, gs, 0);
|
||||||
|
|
||||||
/* Otherwise the toplevel "or" terms hit a bit differently.
|
/* Otherwise the toplevel "or" terms hit a bit differently.
|
||||||
* We first clear hit markers from them.
|
* We first clear hit markers from them.
|
||||||
*/
|
*/
|
||||||
clr_hit_marker(opt->pattern_expression);
|
clr_hit_marker(opt->pattern_expression);
|
||||||
grep_buffer_1(opt, name, buf, size, 1);
|
grep_source_1(opt, gs, 1);
|
||||||
|
|
||||||
if (!chk_hit_marker(opt->pattern_expression))
|
if (!chk_hit_marker(opt->pattern_expression))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return grep_buffer_1(opt, name, buf, size, 0);
|
return grep_source_1(opt, gs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
|
||||||
|
{
|
||||||
|
struct grep_source gs;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL);
|
||||||
|
gs.buf = buf;
|
||||||
|
gs.size = size;
|
||||||
|
|
||||||
|
r = grep_source(opt, &gs);
|
||||||
|
|
||||||
|
grep_source_clear(&gs);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grep_source_init(struct grep_source *gs, enum grep_source_type type,
|
||||||
|
const char *name, const void *identifier)
|
||||||
|
{
|
||||||
|
gs->type = type;
|
||||||
|
gs->name = name ? xstrdup(name) : NULL;
|
||||||
|
gs->buf = NULL;
|
||||||
|
gs->size = 0;
|
||||||
|
gs->driver = NULL;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GREP_SOURCE_FILE:
|
||||||
|
gs->identifier = xstrdup(identifier);
|
||||||
|
break;
|
||||||
|
case GREP_SOURCE_SHA1:
|
||||||
|
gs->identifier = xmalloc(20);
|
||||||
|
memcpy(gs->identifier, identifier, 20);
|
||||||
|
break;
|
||||||
|
case GREP_SOURCE_BUF:
|
||||||
|
gs->identifier = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grep_source_clear(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
free(gs->name);
|
||||||
|
gs->name = NULL;
|
||||||
|
free(gs->identifier);
|
||||||
|
gs->identifier = NULL;
|
||||||
|
grep_source_clear_data(gs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grep_source_clear_data(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
switch (gs->type) {
|
||||||
|
case GREP_SOURCE_FILE:
|
||||||
|
case GREP_SOURCE_SHA1:
|
||||||
|
free(gs->buf);
|
||||||
|
gs->buf = NULL;
|
||||||
|
gs->size = 0;
|
||||||
|
break;
|
||||||
|
case GREP_SOURCE_BUF:
|
||||||
|
/* leave user-provided buf intact */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grep_source_load_sha1(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
enum object_type type;
|
||||||
|
|
||||||
|
grep_read_lock();
|
||||||
|
gs->buf = read_sha1_file(gs->identifier, &type, &gs->size);
|
||||||
|
grep_read_unlock();
|
||||||
|
|
||||||
|
if (!gs->buf)
|
||||||
|
return error(_("'%s': unable to read %s"),
|
||||||
|
gs->name,
|
||||||
|
sha1_to_hex(gs->identifier));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grep_source_load_file(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
const char *filename = gs->identifier;
|
||||||
|
struct stat st;
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (lstat(filename, &st) < 0) {
|
||||||
|
err_ret:
|
||||||
|
if (errno != ENOENT)
|
||||||
|
error(_("'%s': %s"), filename, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!S_ISREG(st.st_mode))
|
||||||
|
return -1;
|
||||||
|
size = xsize_t(st.st_size);
|
||||||
|
i = open(filename, O_RDONLY);
|
||||||
|
if (i < 0)
|
||||||
|
goto err_ret;
|
||||||
|
data = xmalloc(size + 1);
|
||||||
|
if (st.st_size != read_in_full(i, data, size)) {
|
||||||
|
error(_("'%s': short read %s"), filename, strerror(errno));
|
||||||
|
close(i);
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(i);
|
||||||
|
data[size] = 0;
|
||||||
|
|
||||||
|
gs->buf = data;
|
||||||
|
gs->size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int grep_source_load(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
if (gs->buf)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (gs->type) {
|
||||||
|
case GREP_SOURCE_FILE:
|
||||||
|
return grep_source_load_file(gs);
|
||||||
|
case GREP_SOURCE_SHA1:
|
||||||
|
return grep_source_load_sha1(gs);
|
||||||
|
case GREP_SOURCE_BUF:
|
||||||
|
return gs->buf ? 0 : -1;
|
||||||
|
}
|
||||||
|
die("BUG: invalid grep_source type");
|
||||||
|
}
|
||||||
|
|
||||||
|
void grep_source_load_driver(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
if (gs->driver)
|
||||||
|
return;
|
||||||
|
|
||||||
|
grep_attr_lock();
|
||||||
|
gs->driver = userdiff_find_by_path(gs->name);
|
||||||
|
if (!gs->driver)
|
||||||
|
gs->driver = userdiff_find_by_name("default");
|
||||||
|
grep_attr_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int grep_source_is_binary(struct grep_source *gs)
|
||||||
|
{
|
||||||
|
grep_source_load_driver(gs);
|
||||||
|
if (gs->driver->binary != -1)
|
||||||
|
return gs->driver->binary;
|
||||||
|
|
||||||
|
if (!grep_source_load(gs))
|
||||||
|
return buffer_is_binary(gs->buf, gs->size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
48
grep.h
48
grep.h
|
@ -9,6 +9,7 @@ typedef int pcre_extra;
|
||||||
#endif
|
#endif
|
||||||
#include "kwset.h"
|
#include "kwset.h"
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
#include "userdiff.h"
|
||||||
|
|
||||||
enum grep_pat_token {
|
enum grep_pat_token {
|
||||||
GREP_PATTERN,
|
GREP_PATTERN,
|
||||||
|
@ -116,7 +117,6 @@ struct grep_opt {
|
||||||
int show_hunk_mark;
|
int show_hunk_mark;
|
||||||
int file_break;
|
int file_break;
|
||||||
int heading;
|
int heading;
|
||||||
int use_threads;
|
|
||||||
void *priv;
|
void *priv;
|
||||||
|
|
||||||
void (*output)(struct grep_opt *opt, const void *data, size_t size);
|
void (*output)(struct grep_opt *opt, const void *data, size_t size);
|
||||||
|
@ -128,7 +128,33 @@ extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const cha
|
||||||
extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
|
extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
|
||||||
extern void compile_grep_patterns(struct grep_opt *opt);
|
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, char *buf, unsigned long size);
|
||||||
|
|
||||||
|
struct grep_source {
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
enum grep_source_type {
|
||||||
|
GREP_SOURCE_SHA1,
|
||||||
|
GREP_SOURCE_FILE,
|
||||||
|
GREP_SOURCE_BUF,
|
||||||
|
} type;
|
||||||
|
void *identifier;
|
||||||
|
|
||||||
|
char *buf;
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
struct userdiff_driver *driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
void grep_source_init(struct grep_source *gs, enum grep_source_type type,
|
||||||
|
const char *name, const void *identifier);
|
||||||
|
int grep_source_load(struct grep_source *gs);
|
||||||
|
void grep_source_clear_data(struct grep_source *gs);
|
||||||
|
void grep_source_clear(struct grep_source *gs);
|
||||||
|
void grep_source_load_driver(struct grep_source *gs);
|
||||||
|
int grep_source_is_binary(struct grep_source *gs);
|
||||||
|
|
||||||
|
int grep_source(struct grep_opt *opt, struct grep_source *gs);
|
||||||
|
|
||||||
extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
|
extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
|
||||||
extern int grep_threads_ok(const struct grep_opt *opt);
|
extern int grep_threads_ok(const struct grep_opt *opt);
|
||||||
|
@ -138,7 +164,25 @@ extern int grep_threads_ok(const struct grep_opt *opt);
|
||||||
* Mutex used around access to the attributes machinery if
|
* Mutex used around access to the attributes machinery if
|
||||||
* opt->use_threads. Must be initialized/destroyed by callers!
|
* opt->use_threads. Must be initialized/destroyed by callers!
|
||||||
*/
|
*/
|
||||||
|
extern int grep_use_locks;
|
||||||
extern pthread_mutex_t grep_attr_mutex;
|
extern pthread_mutex_t grep_attr_mutex;
|
||||||
|
extern pthread_mutex_t grep_read_mutex;
|
||||||
|
|
||||||
|
static inline void grep_read_lock(void)
|
||||||
|
{
|
||||||
|
if (grep_use_locks)
|
||||||
|
pthread_mutex_lock(&grep_read_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void grep_read_unlock(void)
|
||||||
|
{
|
||||||
|
if (grep_use_locks)
|
||||||
|
pthread_mutex_unlock(&grep_read_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define grep_read_lock()
|
||||||
|
#define grep_read_unlock()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2149,7 +2149,6 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
||||||
if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
|
if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
|
||||||
return 1;
|
return 1;
|
||||||
return grep_buffer(&opt->grep_filter,
|
return grep_buffer(&opt->grep_filter,
|
||||||
NULL, /* we say nothing, not even filename */
|
|
||||||
commit->buffer, strlen(commit->buffer));
|
commit->buffer, strlen(commit->buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,4 +99,28 @@ test_expect_success 'git grep y<NUL>x a' "
|
||||||
test_must_fail git grep -f f a
|
test_must_fail git grep -f f a
|
||||||
"
|
"
|
||||||
|
|
||||||
|
test_expect_success 'grep respects binary diff attribute' '
|
||||||
|
echo text >t &&
|
||||||
|
git add t &&
|
||||||
|
echo t:text >expect &&
|
||||||
|
git grep text t >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
echo "t -diff" >.gitattributes &&
|
||||||
|
echo "Binary file t matches" >expect &&
|
||||||
|
git grep text t >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep respects not-binary diff attribute' '
|
||||||
|
echo binQary | q_to_nul >b &&
|
||||||
|
git add b &&
|
||||||
|
echo "Binary file b matches" >expect &&
|
||||||
|
git grep bin b >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
echo "b diff" >.gitattributes &&
|
||||||
|
echo "b:binQary" >expect &&
|
||||||
|
git grep bin b | nul_to_q >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Загрузка…
Ссылка в новой задаче