#include "attr.h"
#include "run-command.h"
#include "utf8.h"
+#include "userdiff.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
"\033[41m", /* WHITESPACE (red background) */
};
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
static int parse_diff_color_slot(const char *var, int ofs)
{
if (!strcasecmp(var+ofs, "plain"))
die("bad config variable '%s'", var);
}
-static struct ll_diff_driver {
- const char *name;
- struct ll_diff_driver *next;
- const char *cmd;
-} *user_diff, **user_diff_tail;
-
-/*
- * Currently there is only "diff.<drivername>.command" variable;
- * because there are "diff.color.<slot>" variables, we are parsing
- * this in a bit convoluted way to allow low level diff driver
- * called "color".
- */
-static int parse_lldiff_command(const char *var, const char *ep, const char *value)
-{
- const char *name;
- int namelen;
- struct ll_diff_driver *drv;
-
- name = var + 5;
- namelen = ep - name;
- for (drv = user_diff; drv; drv = drv->next)
- if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
- break;
- if (!drv) {
- drv = xcalloc(1, sizeof(struct ll_diff_driver));
- drv->name = xmemdupz(name, namelen);
- if (!user_diff_tail)
- user_diff_tail = &user_diff;
- *user_diff_tail = drv;
- user_diff_tail = &(drv->next);
- }
-
- return git_config_string(&(drv->cmd), var, value);
-}
-
-/*
- * 'diff.<what>.funcname' attribute can be specified in the configuration
- * to define a customized regexp to find the beginning of a function to
- * be used for hunk header lines of "diff -p" style output.
- */
-static struct funcname_pattern {
- char *name;
- char *pattern;
- struct funcname_pattern *next;
-} *funcname_pattern_list;
-
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
-{
- const char *name;
- int namelen;
- struct funcname_pattern *pp;
-
- name = var + 5; /* "diff." */
- namelen = ep - name;
-
- for (pp = funcname_pattern_list; pp; pp = pp->next)
- if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
- break;
- if (!pp) {
- pp = xcalloc(1, sizeof(*pp));
- pp->name = xmemdupz(name, namelen);
- pp->next = funcname_pattern_list;
- funcname_pattern_list = pp;
- }
- free(pp->pattern);
- pp->pattern = xstrdup(value);
- return 0;
-}
-
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
}
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
- if (!prefixcmp(var, "diff.")) {
- const char *ep = strrchr(var, '.');
-
- if (ep != var + 4 && !strcmp(ep, ".command"))
- return parse_lldiff_command(var, ep, value);
- }
return git_diff_basic_config(var, value, cb);
}
return 0;
}
+ switch (userdiff_config(var, value)) {
+ case 0: break;
+ case -1: return -1;
+ default: return 0;
+ }
+
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
if (!value)
return 0;
}
- if (!prefixcmp(var, "diff.")) {
- const char *ep = strrchr(var, '.');
- if (ep != var + 4) {
- if (!strcmp(ep, ".funcname")) {
- if (!value)
- return config_error_nonbool(var);
- return parse_funcname_pattern(var, ep, value);
- }
- }
- }
-
return git_color_default_config(var, value, cb);
}
{
int need_one = quote_c_style(one, NULL, NULL, 1);
int need_two = quote_c_style(two, NULL, NULL, 1);
- struct strbuf res;
+ struct strbuf res = STRBUF_INIT;
- strbuf_init(&res, 0);
if (need_one + need_two) {
strbuf_addch(&res, '"');
quote_c_style(one, &res, NULL, 1);
}
else if (diff_populate_filespec(one, 0))
return -1;
+
mf->ptr = one->data;
mf->size = one->size;
return 0;
mmfile_t minus, plus;
int i;
+ memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
{
const char *old = a;
const char *new = b;
- struct strbuf name;
+ struct strbuf name = STRBUF_INIT;
int pfx_length, sfx_length;
int len_a = strlen(a);
int len_b = strlen(b);
int qlen_a = quote_c_style(a, NULL, NULL, 0);
int qlen_b = quote_c_style(b, NULL, NULL, 0);
- strbuf_init(&name, 0);
if (qlen_a || qlen_b) {
quote_c_style(a, &name, NULL, 0);
strbuf_addstr(&name, " => ");
return;
if (!file->is_renamed) {
- struct strbuf buf;
- strbuf_init(&buf, 0);
+ struct strbuf buf = STRBUF_INIT;
if (quote_c_style(file->name, &buf, NULL, 0)) {
pname = strbuf_detach(&buf, NULL);
} else {
/*
* Original minus copied is the removed material,
* added is the new material. They are both damages
- * made to the preimage.
+ * made to the preimage. In --dirstat-by-file mode, count
+ * damaged files, not damaged lines. This is done by
+ * counting only a single damaged line per file.
*/
damage = (p->one->size - copied) + added;
+ if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
+ damage = 1;
ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
dir.files[dir.nr].name = name;
emit_binary_diff_body(file, two, one);
}
-static void setup_diff_attr_check(struct git_attr_check *check)
-{
- static struct git_attr *attr_diff;
-
- if (!attr_diff) {
- attr_diff = git_attr("diff", 4);
- }
- check[0].attr = attr_diff;
-}
-
-static void diff_filespec_check_attr(struct diff_filespec *one)
+static void diff_filespec_load_driver(struct diff_filespec *one)
{
- struct git_attr_check attr_diff_check;
- int check_from_data = 0;
-
- if (one->checked_attr)
- return;
-
- setup_diff_attr_check(&attr_diff_check);
- one->is_binary = 0;
- one->funcname_pattern_ident = NULL;
-
- if (!git_checkattr(one->path, 1, &attr_diff_check)) {
- const char *value;
-
- /* binaryness */
- value = attr_diff_check.value;
- if (ATTR_TRUE(value))
- ;
- else if (ATTR_FALSE(value))
- one->is_binary = 1;
- else
- check_from_data = 1;
-
- /* funcname pattern ident */
- if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
- ;
- else
- one->funcname_pattern_ident = value;
- }
-
- if (check_from_data) {
- if (!one->data && DIFF_FILE_VALID(one))
- diff_populate_filespec(one, 0);
-
- if (one->data)
- one->is_binary = buffer_is_binary(one->data, one->size);
- }
+ if (!one->driver)
+ one->driver = userdiff_find_by_path(one->path);
+ if (!one->driver)
+ one->driver = userdiff_find_by_name("default");
}
int diff_filespec_is_binary(struct diff_filespec *one)
{
- diff_filespec_check_attr(one);
+ if (one->is_binary == -1) {
+ diff_filespec_load_driver(one);
+ if (one->driver->binary != -1)
+ one->is_binary = one->driver->binary;
+ else {
+ if (!one->data && DIFF_FILE_VALID(one))
+ diff_populate_filespec(one, 0);
+ if (one->data)
+ one->is_binary = buffer_is_binary(one->data,
+ one->size);
+ if (one->is_binary == -1)
+ one->is_binary = 0;
+ }
+ }
return one->is_binary;
}
-static const char *funcname_pattern(const char *ident)
+static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
{
- struct funcname_pattern *pp;
-
- for (pp = funcname_pattern_list; pp; pp = pp->next)
- if (!strcmp(ident, pp->name))
- return pp->pattern;
- return NULL;
-}
-
-static struct builtin_funcname_pattern {
- const char *name;
- const char *pattern;
-} builtin_funcname_pattern[] = {
- { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
- { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" },
- { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
- "new\\|return\\|switch\\|throw\\|while\\)\n"
- "^[ ]*\\(\\([ ]*"
- "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
- "[ ]*([^;]*\\)$" },
- { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|"
- "destructor\\|interface\\|implementation\\|"
- "initialization\\|finalization\\)[ \t]*.*\\)$"
- "\\|"
- "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
- },
- { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)" },
- { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
- { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
- { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
-};
-
-static const char *diff_funcname_pattern(struct diff_filespec *one)
-{
- const char *ident, *pattern;
- int i;
-
- diff_filespec_check_attr(one);
- ident = one->funcname_pattern_ident;
-
- if (!ident)
- /*
- * If the config file has "funcname.default" defined, that
- * regexp is used; otherwise NULL is returned and xemit uses
- * the built-in default.
- */
- return funcname_pattern("default");
-
- /* Look up custom "funcname.$ident" regexp from config. */
- pattern = funcname_pattern(ident);
- if (pattern)
- return pattern;
-
- /*
- * And define built-in fallback patterns here. Note that
- * these can be overridden by the user's config settings.
- */
- for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
- if (!strcmp(ident, builtin_funcname_pattern[i].name))
- return builtin_funcname_pattern[i].pattern;
-
- return NULL;
+ diff_filespec_load_driver(one);
+ return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
}
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
options->b_prefix = b;
}
+static const char *get_textconv(struct diff_filespec *one)
+{
+ if (!DIFF_FILE_VALID(one))
+ return NULL;
+ if (!S_ISREG(one->mode))
+ return NULL;
+ diff_filespec_load_driver(one);
+ return one->driver->textconv;
+}
+
static void builtin_diff(const char *name_a,
const char *name_b,
struct diff_filespec *one,
const char *set = diff_get_color_opt(o, DIFF_METAINFO);
const char *reset = diff_get_color_opt(o, DIFF_RESET);
const char *a_prefix, *b_prefix;
+ const char *textconv_one = NULL, *textconv_two = NULL;
diff_set_mnemonic_prefix(o, "a/", "b/");
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
b_prefix = o->b_prefix;
}
+ /* Never use a non-valid filename anywhere if at all possible */
+ name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
+ name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
+
a_one = quote_two(a_prefix, name_a + (*name_a == '/'));
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
+ if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+ textconv_one = get_textconv(one);
+ textconv_two = get_textconv(two);
+ }
+
if (!DIFF_OPT_TST(o, TEXT) &&
- (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
+ ( (diff_filespec_is_binary(one) && !textconv_one) ||
+ (diff_filespec_is_binary(two) && !textconv_two) )) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
xdemitconf_t xecfg;
xdemitcb_t ecb;
struct emit_callback ecbdata;
- const char *funcname_pattern;
+ const struct userdiff_funcname *pe;
+
+ if (textconv_one) {
+ size_t size;
+ mf1.ptr = run_textconv(textconv_one, one, &size);
+ if (!mf1.ptr)
+ die("unable to read files to diff");
+ mf1.size = size;
+ }
+ if (textconv_two) {
+ size_t size;
+ mf2.ptr = run_textconv(textconv_two, two, &size);
+ if (!mf2.ptr)
+ die("unable to read files to diff");
+ mf2.size = size;
+ }
- funcname_pattern = diff_funcname_pattern(one);
- if (!funcname_pattern)
- funcname_pattern = diff_funcname_pattern(two);
+ pe = diff_funcname_pattern(one);
+ if (!pe)
+ pe = diff_funcname_pattern(two);
+ memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
- if (funcname_pattern)
- xdiff_set_find_func(&xecfg, funcname_pattern);
+ if (pe)
+ xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
if (!diffopts)
;
else if (!prefixcmp(diffopts, "--unified="))
&xpp, &xecfg, &ecb);
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
free_diff_words_data(&ecbdata);
+ if (textconv_one)
+ free(mf1.ptr);
+ if (textconv_two)
+ free(mf2.ptr);
}
free_ab_and_return:
xdemitconf_t xecfg;
xdemitcb_t ecb;
+ memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
xdemitconf_t xecfg;
xdemitcb_t ecb;
+ memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
xpp.flags = XDF_NEED_MINIMAL;
spec->path = (char *)(spec + 1);
memcpy(spec->path, path, namelen+1);
spec->count = 1;
+ spec->is_binary = -1;
return spec;
}
static int populate_from_stdin(struct diff_filespec *s)
{
- struct strbuf buf;
+ struct strbuf buf = STRBUF_INIT;
size_t size = 0;
- strbuf_init(&buf, 0);
if (strbuf_read(&buf, 0, 0) < 0)
return error("error while reading from stdin %s",
strerror(errno));
if (!s->sha1_valid ||
reuse_worktree_file(s->path, s->sha1, 0)) {
- struct strbuf buf;
+ struct strbuf buf = STRBUF_INIT;
struct stat st;
int fd;
/*
* Convert from working tree format to canonical git format
*/
- strbuf_init(&buf, 0);
if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
size_t size = 0;
munmap(s->data, s->size);
}
}
-static const char *external_diff_attr(const char *name)
-{
- struct git_attr_check attr_diff_check;
-
- if (!name)
- return NULL;
-
- setup_diff_attr_check(&attr_diff_check);
- if (!git_checkattr(name, 1, &attr_diff_check)) {
- const char *value = attr_diff_check.value;
- if (!ATTR_TRUE(value) &&
- !ATTR_FALSE(value) &&
- !ATTR_UNSET(value)) {
- struct ll_diff_driver *drv;
-
- for (drv = user_diff; drv; drv = drv->next)
- if (!strcmp(drv->name, value))
- return drv->cmd;
- }
- }
- return NULL;
-}
-
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
else {
- const char *cmd = external_diff_attr(attr_path);
- if (cmd)
- pgm = cmd;
+ struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+ if (drv && drv->external)
+ pgm = drv->external;
}
if (pgm) {
else if (!strcmp(arg, "--cumulative")) {
options->output_format |= DIFF_FORMAT_DIRSTAT;
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+ } else if (opt_arg(arg, 0, "dirstat-by-file",
+ &options->dirstat_percent)) {
+ options->output_format |= DIFF_FORMAT_DIRSTAT;
+ DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
}
else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF;
}
struct patch_id_t {
- SHA_CTX *ctx;
+ git_SHA_CTX *ctx;
int patchlen;
};
new_len = remove_space(line, len);
- SHA1_Update(data->ctx, line, new_len);
+ git_SHA1_Update(data->ctx, line, new_len);
data->patchlen += new_len;
}
{
struct diff_queue_struct *q = &diff_queued_diff;
int i;
- SHA_CTX ctx;
+ git_SHA_CTX ctx;
struct patch_id_t data;
char buffer[PATH_MAX * 4 + 20];
- SHA1_Init(&ctx);
+ git_SHA1_Init(&ctx);
memset(&data, 0, sizeof(struct patch_id_t));
data.ctx = &ctx;
struct diff_filepair *p = q->queue[i];
int len1, len2;
+ memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
if (p->status == 0)
return error("internal diff status error");
len2, p->two->path,
len1, p->one->path,
len2, p->two->path);
- SHA1_Update(&ctx, buffer, len1);
+ git_SHA1_Update(&ctx, buffer, len1);
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = 3;
&xpp, &xecfg, &ecb);
}
- SHA1_Final(sha1, &ctx);
+ git_SHA1_Final(sha1, &ctx);
return 0;
}
fill_filespec(one, sha1, mode);
diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
}
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+ size_t *outsize)
+{
+ struct diff_tempfile temp;
+ const char *argv[3];
+ const char **arg = argv;
+ struct child_process child;
+ struct strbuf buf = STRBUF_INIT;
+
+ prepare_temp_file(spec->path, &temp, spec);
+ *arg++ = pgm;
+ *arg++ = temp.name;
+ *arg = NULL;
+
+ memset(&child, 0, sizeof(child));
+ child.argv = argv;
+ child.out = -1;
+ if (start_command(&child) != 0 ||
+ strbuf_read(&buf, child.out, 0) < 0 ||
+ finish_command(&child) != 0) {
+ if (temp.name == temp.tmp_path)
+ unlink(temp.name);
+ error("error running textconv command '%s'", pgm);
+ return NULL;
+ }
+ if (temp.name == temp.tmp_path)
+ unlink(temp.name);
+
+ return strbuf_detach(&buf, outsize);
+}