static int diff_detect_rename_default;
static int diff_rename_limit_default = 200;
+static int diff_suppress_blank_empty;
int diff_use_color_default = -1;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
+static int diff_mnemonic_prefix;
static char diff_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
* 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 {
+struct funcname_pattern_entry {
char *name;
char *pattern;
- struct funcname_pattern *next;
+ int cflags;
+};
+static struct funcname_pattern_list {
+ struct funcname_pattern_list *next;
+ struct funcname_pattern_entry e;
} *funcname_pattern_list;
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
+static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
{
const char *name;
int namelen;
- struct funcname_pattern *pp;
+ struct funcname_pattern_list *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])
+ if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
break;
if (!pp) {
pp = xcalloc(1, sizeof(*pp));
- pp->name = xmemdupz(name, namelen);
+ pp->e.name = xmemdupz(name, namelen);
pp->next = funcname_pattern_list;
funcname_pattern_list = pp;
}
- free(pp->pattern);
- pp->pattern = xstrdup(value);
+ free(pp->e.pattern);
+ pp->e.pattern = xstrdup(value);
+ pp->e.cflags = cflags;
return 0;
}
diff_auto_refresh_index = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.mnemonicprefix")) {
+ diff_mnemonic_prefix = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
if (!prefixcmp(var, "diff.")) {
return 0;
}
+ /* like GNU diff's --suppress-blank-empty option */
+ if (!strcmp(var, "diff.suppress-blank-empty")) {
+ diff_suppress_blank_empty = git_config_bool(var, 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 parse_funcname_pattern(var, ep, value,
+ 0);
+ } else if (!strcmp(ep, ".xfuncname")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return parse_funcname_pattern(var, ep, value,
+ REG_EXTENDED);
}
}
}
{
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);
const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
const char *reset = diff_get_color(color_diff, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
+ const char *a_prefix, *b_prefix;
+
+ if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ a_prefix = o->b_prefix;
+ b_prefix = o->a_prefix;
+ } else {
+ a_prefix = o->a_prefix;
+ b_prefix = o->b_prefix;
+ }
name_a += (*name_a == '/');
name_b += (*name_b == '/');
strbuf_reset(&a_name);
strbuf_reset(&b_name);
- quote_two_c_style(&a_name, o->a_prefix, name_a, 0);
- quote_two_c_style(&b_name, o->b_prefix, name_b, 0);
+ quote_two_c_style(&a_name, a_prefix, name_a, 0);
+ quote_two_c_style(&b_name, b_prefix, name_b, 0);
diff_populate_filespec(one, 0);
diff_populate_filespec(two, 0);
}
struct diff_words_data {
- struct xdiff_emit_state xm;
struct diff_words_buffer minus, plus;
FILE *file;
};
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
- ecb.outf = xdiff_outf;
- ecb.priv = diff_words;
- diff_words->xm.consume = fn_out_diff_words_aux;
- xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
+ xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+ &xpp, &xecfg, &ecb);
free(minus.ptr);
free(plus.ptr);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
struct emit_callback {
- struct xdiff_emit_state xm;
int nparents, color_diff;
unsigned ws_rule;
sane_truncate_fn truncate;
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
+ if (diff_suppress_blank_empty
+ && len == 2 && line[0] == ' ' && line[1] == '\n') {
+ line[0] = '\n';
+ len = 1;
+ }
+
/* This is not really necessary for now because
* this codepath only deals with two-way diffs.
*/
{
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, " => ");
}
struct diffstat_t {
- struct xdiff_emit_state xm;
-
int nr;
int alloc;
struct diffstat_file {
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;
}
struct checkdiff_t {
- struct xdiff_emit_state xm;
const char *filename;
int lineno;
struct diff_options *o;
return one->is_binary;
}
-static const char *funcname_pattern(const char *ident)
+static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
{
- struct funcname_pattern *pp;
+ struct funcname_pattern_list *pp;
for (pp = funcname_pattern_list; pp; pp = pp->next)
- if (!strcmp(ident, pp->name))
- return pp->pattern;
+ if (!strcmp(ident, pp->e.name))
+ return &pp->e;
return NULL;
}
-static struct builtin_funcname_pattern {
- const char *name;
- const char *pattern;
-} builtin_funcname_pattern[] = {
- { "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\\).*\\)$"
- },
- { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
- { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
- { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
+static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
+ { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+ REG_EXTENDED },
+ { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED },
+ { "java",
+ "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+ "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+ REG_EXTENDED },
+ { "objc",
+ /* Negate C statements that can look like functions */
+ "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+ /* Objective-C methods */
+ "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+ /* C functions */
+ "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+ /* Objective-C class/protocol definitions */
+ "^(@(implementation|interface|protocol)[ \t].*)$",
+ REG_EXTENDED },
+ { "pascal",
+ "^((procedure|function|constructor|destructor|interface|"
+ "implementation|initialization|finalization)[ \t]*.*)$"
+ "\n"
+ "^(.*=[ \t]*(class|record).*)$",
+ REG_EXTENDED },
+ { "php", "^[\t ]*((function|class).*)", REG_EXTENDED },
+ { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED },
+ { "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+ REG_EXTENDED },
+ { "tex",
+ "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+ REG_EXTENDED },
};
-static const char *diff_funcname_pattern(struct diff_filespec *one)
+static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
{
- const char *ident, *pattern;
+ const char *ident;
+ const struct funcname_pattern_entry *pe;
int i;
diff_filespec_check_attr(one);
return funcname_pattern("default");
/* Look up custom "funcname.$ident" regexp from config. */
- pattern = funcname_pattern(ident);
- if (pattern)
- return pattern;
+ pe = funcname_pattern(ident);
+ if (pe)
+ return pe;
/*
* And define built-in fallback patterns here. Note that
*/
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 &builtin_funcname_pattern[i];
return NULL;
}
+void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
+{
+ if (!options->a_prefix)
+ options->a_prefix = a;
+ if (!options->b_prefix)
+ options->b_prefix = b;
+}
+
static void builtin_diff(const char *name_a,
const char *name_b,
struct diff_filespec *one,
char *a_one, *b_two;
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;
- a_one = quote_two(o->a_prefix, name_a + (*name_a == '/'));
- b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
+ diff_set_mnemonic_prefix(o, "a/", "b/");
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ a_prefix = o->b_prefix;
+ b_prefix = o->a_prefix;
+ } else {
+ a_prefix = o->a_prefix;
+ 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";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
xdemitconf_t xecfg;
xdemitcb_t ecb;
struct emit_callback ecbdata;
- const char *funcname_pattern;
+ const struct funcname_pattern_entry *pe;
- 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(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
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="))
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!prefixcmp(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- ecb.outf = xdiff_outf;
- ecb.priv = &ecbdata;
- ecbdata.xm.consume = fn_out_consume;
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
}
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+ &xpp, &xecfg, &ecb);
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
free_diff_words_data(&ecbdata);
}
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
- ecb.outf = xdiff_outf;
- ecb.priv = diffstat;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+ &xpp, &xecfg, &ecb);
}
free_and_return:
return;
memset(&data, 0, sizeof(data));
- data.xm.consume = checkdiff_consume;
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
data.o = o;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
xpp.flags = XDF_NEED_MINIMAL;
- ecb.outf = xdiff_outf;
- ecb.priv = &data;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+ &xpp, &xecfg, &ecb);
if ((data.ws_rule & WS_TRAILING_SPACE) &&
data.trailing_blanks_start) {
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);
DIFF_OPT_CLR(options, COLOR_DIFF);
options->detect_rename = diff_detect_rename_default;
- options->a_prefix = "a/";
- options->b_prefix = "b/";
+ if (!diff_mnemonic_prefix) {
+ options->a_prefix = "a/";
+ options->b_prefix = "b/";
+ }
}
int diff_setup_done(struct diff_options *options)
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 {
- struct xdiff_emit_state xm;
- 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;
- data.xm.consume = patch_id_consume;
for (i = 0; i < q->nr; i++) {
xpparam_t xpp;
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;
xecfg.flags = XDL_EMIT_FUNCNAMES;
- ecb.outf = xdiff_outf;
- ecb.priv = &data;
- xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+ &xpp, &xecfg, &ecb);
}
- SHA1_Final(sha1, &ctx);
+ git_SHA1_Final(sha1, &ctx);
return 0;
}
struct diffstat_t diffstat;
memset(&diffstat, 0, sizeof(struct diffstat_t));
- diffstat.xm.consume = diffstat_consume;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
if (check_pair_status(p))