*
* No surprises, and works with signed and unsigned chars.
*/
-#include "cache.h"
+#include "git-compat-util.h"
enum {
S = GIT_SPACE,
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */
/* Nothing in the 128.. range */
};
+
+ /* For case-insensitive kwset */
+ const char tolower_trans_tbl[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ' ', '!', '"', '#', '$', '%', '&', 0x27,
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', '[', 0x5c, ']', '^', '_',
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', '{', '|', '}', '~', 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ };
struct strbuf;
struct diff_filespec;
struct userdiff_driver;
+struct sha1_array;
+struct commit;
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
#define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28)
#define DIFF_OPT_FUNCCONTEXT (1 << 29)
+ #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
int stat_width;
int stat_name_width;
+ int stat_graph_width;
int stat_count;
const char *word_regex;
enum diff_words_type word_diff;
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *);
-extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
+extern void diff_tree_combined(const unsigned char *sha1, const struct sha1_array *parents, int dense, struct rev_info *rev);
-extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
+extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev);
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
extern int parse_rename_score(const char **cp_p);
+extern int print_stat_summary(FILE *fp, int files,
+ int insertions, int deletions);
+
#endif /* DIFF_H */
#else
#include <poll.h>
#endif
-#ifndef __MINGW32__
+#if defined(__MINGW32__)
+/* pull in Windows compatibility stuff */
+#include "compat/mingw.h"
+#elif defined(_MSC_VER)
+#include "compat/msvc.h"
+#else
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
+#include <sys/un.h>
#ifndef NO_INTTYPES_H
#include <inttypes.h>
#else
#include <grp.h>
#define _ALL_SOURCE 1
#endif
-#else /* __MINGW32__ */
-/* pull in Windows compatibility stuff */
-#include "compat/mingw.h"
-#endif /* __MINGW32__ */
-#ifdef _MSC_VER
-#include "compat/msvc.h"
#endif
#ifndef NO_LIBGEN_H
return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
}
+ /* in ctype.c, for kwset users */
+ extern const char tolower_trans_tbl[256];
+
/* Sane ctype - no locale, and works with signed chars */
#undef isascii
#undef isspace
#undef isdigit
#undef isalpha
#undef isalnum
+#undef islower
+#undef isupper
#undef tolower
#undef toupper
extern unsigned char sane_ctype[256];
#define isdigit(x) sane_istest(x,GIT_DIGIT)
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define islower(x) sane_iscase(x, 1)
+#define isupper(x) sane_iscase(x, 0)
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
return x;
}
+static inline int sane_iscase(int x, int is_lower)
+{
+ if (!sane_istest(x, GIT_ALPHA))
+ return 0;
+
+ if (is_lower)
+ return (x & 0x20) != 0;
+ else
+ return (x & 0x20) == 0;
+}
+
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
{
unsigned long ul;
{
const char *error;
int erroffset;
- int options = 0;
+ int options = PCRE_MULTILINE;
if (opt->ignore_case)
options |= PCRE_CASELESS;
p->fixed = 0;
if (p->fixed) {
- if (opt->regflags & REG_ICASE || p->ignore_case) {
- static char trans[256];
- int i;
- for (i = 0; i < 256; i++)
- trans[i] = tolower(i);
- p->kws = kwsalloc(trans);
- } else {
+ if (opt->regflags & REG_ICASE || p->ignore_case)
+ p->kws = kwsalloc(tolower_trans_tbl);
+ else
p->kws = kwsalloc(NULL);
- }
kwsincr(p->kws, p->pattern, p->patternlen);
kwsprep(p->kws);
return;
opt->output(opt, "\n", 1);
}
-static int match_funcname(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(void)
+{
+ if (grep_use_locks)
+ pthread_mutex_lock(&grep_attr_mutex);
+}
+
+static inline void grep_attr_unlock(void)
+{
+ 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()
+#define grep_attr_unlock()
+#endif
+
+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) {
+ if (xecfg && !xecfg->find_func) {
+ 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;
+ }
+ }
+
+ if (xecfg) {
char buf[1];
return xecfg->find_func(bol, eol - bol, buf, 1,
xecfg->find_func_priv) >= 0;
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, 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, bol, end))
+ if (opt->funcbody && !match_funcname(opt, gs, bol, end))
funcname_needed = 2;
if (opt->pre_context < lno)
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, bol, eol)) {
+ if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
funcname_lno = cur;
funcname_needed = 0;
}
/* 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) {
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++;
}
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;
-
- 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,
- 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;
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:
}
memset(&xecfg, 0, sizeof(xecfg));
- if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
- !opt->name_only && !binary_match_only && !collect_hits) {
- struct userdiff_driver *drv = userdiff_find_by_path(name);
- if (drv && drv->funcname.pattern) {
- const struct userdiff_funcname *pe = &drv->funcname;
- xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
- opt->priv = &xecfg;
- }
- }
+ opt->priv = &xecfg;
+
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;
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;
* 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, 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:
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;
}
*/
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));
}
}
-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;
}
void mark_parents_uninteresting(struct commit *commit)
{
- struct commit_list *parents = commit->parents;
+ struct commit_list *parents = NULL, *l;
+
+ for (l = commit->parents; l; l = l->next)
+ commit_list_insert(l->item, &parents);
while (parents) {
struct commit *commit = parents->item;
- if (!(commit->object.flags & UNINTERESTING)) {
+ l = parents;
+ parents = parents->next;
+ free(l);
+
+ while (commit) {
+ /*
+ * A missing commit is ok iff its parent is marked
+ * uninteresting.
+ *
+ * We just mark such a thing parsed, so that when
+ * it is popped next time around, we won't be trying
+ * to parse it and get an error.
+ */
+ if (!has_sha1_file(commit->object.sha1))
+ commit->object.parsed = 1;
+
+ if (commit->object.flags & UNINTERESTING)
+ break;
+
commit->object.flags |= UNINTERESTING;
/*
* wasn't uninteresting), in which case we need
* to mark its parents recursively too..
*/
- if (commit->parents)
- mark_parents_uninteresting(commit);
- }
+ if (!commit->parents)
+ break;
- /*
- * A missing commit is ok iff its parent is marked
- * uninteresting.
- *
- * We just mark such a thing parsed, so that when
- * it is popped next time around, we won't be trying
- * to parse it and get an error.
- */
- if (!has_sha1_file(commit->object.sha1))
- commit->object.parsed = 1;
- parents = parents->next;
+ for (l = commit->parents->next; l; l = l->next)
+ commit_list_insert(l->item, &parents);
+ commit = commit->parents->item;
+ }
}
}
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp, *parent;
- int tree_changed = 0, tree_same = 0;
+ int tree_changed = 0, tree_same = 0, nth_parent = 0;
/*
* If we don't do pruning, everything is interesting
while ((parent = *pp) != NULL) {
struct commit *p = parent->item;
+ /*
+ * Do not compare with later parents when we care only about
+ * the first parent chain, in order to avoid derailing the
+ * traversal to follow a side branch that brought everything
+ * in the path we are limited to by the pathspec.
+ */
+ if (revs->first_parent_only && nth_parent++)
+ break;
if (parse_commit(p) < 0)
die("cannot simplify commit %s (because of %s)",
sha1_to_hex(commit->object.sha1),
revs->show_notes = 1;
revs->show_notes_given = 1;
revs->notes_opt.use_default_notes = 1;
+ } else if (!strcmp(arg, "--show-signature")) {
+ revs->show_signature = 1;
} else if (!prefixcmp(arg, "--show-notes=") ||
!prefixcmp(arg, "--notes=")) {
struct strbuf buf = STRBUF_INIT;
revs->grep_filter.regflags |= REG_EXTENDED;
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
revs->grep_filter.regflags |= REG_ICASE;
+ DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
revs->grep_filter.fixed = 1;
} else if (!strcmp(arg, "--all-match")) {
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));
}