зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/grep-binary-attribute' into maint
* 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:
Коммит
6f61eb2017
177
builtin/grep.c
177
builtin/grep.c
|
@ -29,25 +29,12 @@ static int use_threads = 1;
|
|||
#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;
|
||||
struct grep_source source;
|
||||
char done;
|
||||
struct strbuf out;
|
||||
};
|
||||
|
@ -85,21 +72,6 @@ static inline void grep_unlock(void)
|
|||
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. */
|
||||
static pthread_cond_t cond_add;
|
||||
|
||||
|
@ -113,7 +85,8 @@ static pthread_cond_t cond_result;
|
|||
|
||||
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();
|
||||
|
||||
|
@ -121,9 +94,9 @@ static void add_work(enum work_type type, char *name, void *id)
|
|||
pthread_cond_wait(&cond_write, &grep_mutex);
|
||||
}
|
||||
|
||||
todo[todo_end].type = type;
|
||||
todo[todo_end].name = name;
|
||||
todo[todo_end].identifier = id;
|
||||
grep_source_init(&todo[todo_end].source, type, name, id);
|
||||
if (opt->binary != GREP_BINARY_TEXT)
|
||||
grep_source_load_driver(&todo[todo_end].source);
|
||||
todo[todo_end].done = 0;
|
||||
strbuf_reset(&todo[todo_end].out);
|
||||
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
|
||||
|
@ -151,21 +124,6 @@ static struct work_item *get_work(void)
|
|||
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;
|
||||
|
@ -192,8 +150,7 @@ static void work_done(struct work_item *w)
|
|||
|
||||
write_or_die(1, p, len);
|
||||
}
|
||||
free(w->name);
|
||||
free(w->identifier);
|
||||
grep_source_clear(&w->source);
|
||||
}
|
||||
|
||||
if (old_done != todo_done)
|
||||
|
@ -216,25 +173,8 @@ static void *run(void *arg)
|
|||
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);
|
||||
}
|
||||
|
||||
hit |= grep_source(opt, &w->source);
|
||||
grep_source_clear_data(&w->source);
|
||||
work_done(w);
|
||||
}
|
||||
free_grep_patterns(arg);
|
||||
|
@ -254,11 +194,12 @@ static void start_threads(struct grep_opt *opt)
|
|||
int i;
|
||||
|
||||
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_cond_init(&cond_add, NULL);
|
||||
pthread_cond_init(&cond_write, NULL);
|
||||
pthread_cond_init(&cond_result, NULL);
|
||||
grep_use_locks = 1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(todo); i++) {
|
||||
strbuf_init(&todo[i].out, 0);
|
||||
|
@ -302,17 +243,16 @@ static int wait_all(void)
|
|||
}
|
||||
|
||||
pthread_mutex_destroy(&grep_mutex);
|
||||
pthread_mutex_destroy(&read_sha1_mutex);
|
||||
pthread_mutex_destroy(&grep_read_mutex);
|
||||
pthread_mutex_destroy(&grep_attr_mutex);
|
||||
pthread_cond_destroy(&cond_add);
|
||||
pthread_cond_destroy(&cond_write);
|
||||
pthread_cond_destroy(&cond_result);
|
||||
grep_use_locks = 0;
|
||||
|
||||
return hit;
|
||||
}
|
||||
#else /* !NO_PTHREADS */
|
||||
#define read_sha1_lock()
|
||||
#define read_sha1_unlock()
|
||||
|
||||
static int wait_all(void)
|
||||
{
|
||||
|
@ -374,21 +314,9 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type
|
|||
{
|
||||
void *data;
|
||||
|
||||
read_sha1_lock();
|
||||
grep_read_lock();
|
||||
data = read_sha1_file(sha1, type, size);
|
||||
read_sha1_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));
|
||||
|
||||
grep_read_unlock();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -396,7 +324,6 @@ 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) {
|
||||
quote_path_relative(filename + tree_name_len, -1, &pathbuf,
|
||||
|
@ -406,87 +333,51 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
|
|||
strbuf_addstr(&pathbuf, filename);
|
||||
}
|
||||
|
||||
name = strbuf_detach(&pathbuf, NULL);
|
||||
|
||||
#ifndef NO_PTHREADS
|
||||
if (use_threads) {
|
||||
grep_sha1_async(opt, name, sha1);
|
||||
add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
|
||||
strbuf_release(&pathbuf);
|
||||
return 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct grep_source gs;
|
||||
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(name);
|
||||
grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
|
||||
strbuf_release(&pathbuf);
|
||||
hit = grep_source(opt, &gs);
|
||||
|
||||
grep_source_clear(&gs);
|
||||
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)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
char *name;
|
||||
|
||||
if (opt->relative && opt->prefix_length)
|
||||
quote_path_relative(filename, -1, &buf, opt->prefix);
|
||||
else
|
||||
strbuf_addstr(&buf, filename);
|
||||
name = strbuf_detach(&buf, NULL);
|
||||
|
||||
#ifndef NO_PTHREADS
|
||||
if (use_threads) {
|
||||
grep_file_async(opt, name, filename);
|
||||
add_work(opt, GREP_SOURCE_FILE, buf.buf, filename);
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct grep_source gs;
|
||||
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(name);
|
||||
grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename);
|
||||
strbuf_release(&buf);
|
||||
hit = grep_source(opt, &gs);
|
||||
|
||||
grep_source_clear(&gs);
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
@ -615,10 +506,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||
struct strbuf base;
|
||||
int hit, len;
|
||||
|
||||
read_sha1_lock();
|
||||
grep_read_lock();
|
||||
data = read_object_with_reference(obj->sha1, tree_type,
|
||||
&size, NULL);
|
||||
read_sha1_unlock();
|
||||
grep_read_unlock();
|
||||
|
||||
if (!data)
|
||||
die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
|
||||
|
@ -1030,8 +921,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||
use_threads = 0;
|
||||
#endif
|
||||
|
||||
opt.use_threads = use_threads;
|
||||
|
||||
#ifndef NO_PTHREADS
|
||||
if (use_threads) {
|
||||
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
|
||||
int grep_use_locks;
|
||||
|
||||
/*
|
||||
* This lock protects access to the gitattributes machinery, which is
|
||||
* not thread-safe.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as git_attr_mutex, but protecting the thread-unsafe object db access.
|
||||
*/
|
||||
pthread_mutex_t grep_read_mutex;
|
||||
|
||||
#else
|
||||
#define grep_attr_lock(opt)
|
||||
#define grep_attr_unlock(opt)
|
||||
#define grep_attr_lock()
|
||||
#define grep_attr_unlock()
|
||||
#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;
|
||||
if (xecfg && !xecfg->find_func) {
|
||||
struct userdiff_driver *drv;
|
||||
grep_attr_lock(opt);
|
||||
drv = userdiff_find_by_path(name);
|
||||
grep_attr_unlock(opt);
|
||||
if (drv && drv->funcname.pattern) {
|
||||
const struct userdiff_funcname *pe = &drv->funcname;
|
||||
grep_source_load_driver(gs);
|
||||
if (gs->driver->funcname.pattern) {
|
||||
const struct userdiff_funcname *pe = &gs->driver->funcname;
|
||||
xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
|
||||
} else {
|
||||
xecfg = opt->priv = NULL;
|
||||
|
@ -858,33 +863,33 @@ static int match_funcname(struct grep_opt *opt, const char *name, char *bol, cha
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void show_funcname_line(struct grep_opt *opt, const char *name,
|
||||
char *buf, char *bol, unsigned lno)
|
||||
static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
|
||||
char *bol, unsigned lno)
|
||||
{
|
||||
while (bol > buf) {
|
||||
while (bol > gs->buf) {
|
||||
char *eol = --bol;
|
||||
|
||||
while (bol > buf && bol[-1] != '\n')
|
||||
while (bol > gs->buf && bol[-1] != '\n')
|
||||
bol--;
|
||||
lno--;
|
||||
|
||||
if (lno <= opt->last_shown)
|
||||
break;
|
||||
|
||||
if (match_funcname(opt, name, bol, eol)) {
|
||||
show_line(opt, bol, eol, name, lno, '=');
|
||||
if (match_funcname(opt, gs, bol, eol)) {
|
||||
show_line(opt, bol, eol, gs->name, lno, '=');
|
||||
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)
|
||||
{
|
||||
unsigned cur = lno, from = 1, funcname_lno = 0;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* Rewind. */
|
||||
while (bol > buf &&
|
||||
while (bol > gs->buf &&
|
||||
cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
|
||||
char *eol = --bol;
|
||||
|
||||
while (bol > buf && bol[-1] != '\n')
|
||||
while (bol > gs->buf && bol[-1] != '\n')
|
||||
bol--;
|
||||
cur--;
|
||||
if (funcname_needed && match_funcname(opt, name, bol, eol)) {
|
||||
if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
|
||||
funcname_lno = cur;
|
||||
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. */
|
||||
if (opt->funcname && funcname_needed)
|
||||
show_funcname_line(opt, name, buf, bol, cur);
|
||||
show_funcname_line(opt, gs, bol, cur);
|
||||
|
||||
/* Back forward. */
|
||||
while (cur < lno) {
|
||||
|
@ -916,7 +921,7 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
|
|||
|
||||
while (*eol != '\n')
|
||||
eol++;
|
||||
show_line(opt, bol, eol, name, cur, sign);
|
||||
show_line(opt, bol, eol, gs->name, cur, sign);
|
||||
bol = eol + 1;
|
||||
cur++;
|
||||
}
|
||||
|
@ -983,11 +988,10 @@ 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,
|
||||
char *buf, unsigned long size, int collect_hits)
|
||||
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
|
||||
{
|
||||
char *bol = buf;
|
||||
unsigned long left = size;
|
||||
char *bol;
|
||||
unsigned long left;
|
||||
unsigned lno = 1;
|
||||
unsigned last_hit = 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) {
|
||||
case GREP_BINARY_DEFAULT:
|
||||
if (buffer_is_binary(buf, size))
|
||||
if (grep_source_is_binary(gs))
|
||||
binary_match_only = 1;
|
||||
break;
|
||||
case GREP_BINARY_NOMATCH:
|
||||
if (buffer_is_binary(buf, size))
|
||||
if (grep_source_is_binary(gs))
|
||||
return 0; /* Assume unmatch */
|
||||
break;
|
||||
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);
|
||||
|
||||
if (grep_source_load(gs) < 0)
|
||||
return 0;
|
||||
|
||||
bol = gs->buf;
|
||||
left = gs->size;
|
||||
while (left) {
|
||||
char *eol, ch;
|
||||
int hit;
|
||||
|
@ -1083,14 +1092,14 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||
if (opt->status_only)
|
||||
return 1;
|
||||
if (opt->name_only) {
|
||||
show_name(opt, name);
|
||||
show_name(opt, gs->name);
|
||||
return 1;
|
||||
}
|
||||
if (opt->count)
|
||||
goto next_line;
|
||||
if (binary_match_only) {
|
||||
opt->output(opt, "Binary file ", 12);
|
||||
output_color(opt, name, strlen(name),
|
||||
output_color(opt, gs->name, strlen(gs->name),
|
||||
opt->color_filename);
|
||||
opt->output(opt, " matches\n", 9);
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
show_funcname_line(opt, name, buf, bol, lno);
|
||||
show_line(opt, bol, eol, name, lno, ':');
|
||||
show_funcname_line(opt, gs, bol, lno);
|
||||
show_line(opt, bol, eol, gs->name, lno, ':');
|
||||
last_hit = lno;
|
||||
if (opt->funcbody)
|
||||
show_function = 1;
|
||||
goto next_line;
|
||||
}
|
||||
if (show_function && match_funcname(opt, name, bol, eol))
|
||||
if (show_function && match_funcname(opt, gs, bol, eol))
|
||||
show_function = 0;
|
||||
if (show_function ||
|
||||
(last_hit && lno <= last_hit + opt->post_context)) {
|
||||
/* If the last hit is within the post context,
|
||||
* we need to show this line.
|
||||
*/
|
||||
show_line(opt, bol, eol, name, lno, '-');
|
||||
show_line(opt, bol, eol, gs->name, lno, '-');
|
||||
}
|
||||
|
||||
next_line:
|
||||
|
@ -1133,7 +1142,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||
return 0;
|
||||
if (opt->unmatch_name_only) {
|
||||
/* We did not see any hit, so we want to show this */
|
||||
show_name(opt, name);
|
||||
show_name(opt, gs->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1156,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
|||
*/
|
||||
if (opt->count && count) {
|
||||
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, ':');
|
||||
snprintf(buf, sizeof(buf), "%u\n", count);
|
||||
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
|
||||
* buffer-wide "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.
|
||||
* We first clear hit markers from them.
|
||||
*/
|
||||
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))
|
||||
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
|
||||
#include "kwset.h"
|
||||
#include "thread-utils.h"
|
||||
#include "userdiff.h"
|
||||
|
||||
enum grep_pat_token {
|
||||
GREP_PATTERN,
|
||||
|
@ -116,7 +117,6 @@ struct grep_opt {
|
|||
int show_hunk_mark;
|
||||
int file_break;
|
||||
int heading;
|
||||
int use_threads;
|
||||
void *priv;
|
||||
|
||||
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 compile_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 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
|
||||
* 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_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
|
||||
|
|
|
@ -2136,7 +2136,6 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
|||
if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
|
||||
return 1;
|
||||
return grep_buffer(&opt->grep_filter,
|
||||
NULL, /* we say nothing, not even filename */
|
||||
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_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
|
||||
|
|
Загрузка…
Ссылка в новой задаче