buffer->text.ptr[buffer->text.size] = '\0';
}
+struct diff_words_style_elem
+{
+ const char *prefix;
+ const char *suffix;
+ const char *color; /* NULL; filled in by the setup code if
+ * color is enabled */
+};
+
+struct diff_words_style
+{
+ enum diff_words_type type;
+ struct diff_words_style_elem new, old, ctx;
+ const char *newline;
+};
+
+struct diff_words_style diff_words_styles[] = {
+ { DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
+ { DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
+ { DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
+};
+
struct diff_words_data {
struct diff_words_buffer minus, plus;
const char *current_plus;
FILE *file;
regex_t *word_regex;
+ enum diff_words_type type;
+ struct diff_words_style *style;
};
+static int fn_out_diff_words_write_helper(FILE *fp,
+ struct diff_words_style_elem *st_el,
+ const char *newline,
+ size_t count, const char *buf)
+{
+ while (count) {
+ char *p = memchr(buf, '\n', count);
+ if (p != buf) {
+ if (st_el->color && fputs(st_el->color, fp) < 0)
+ return -1;
+ if (fputs(st_el->prefix, fp) < 0 ||
+ fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+ fputs(st_el->suffix, fp) < 0)
+ return -1;
+ if (st_el->color && *st_el->color
+ && fputs(GIT_COLOR_RESET, fp) < 0)
+ return -1;
+ }
+ if (!p)
+ return 0;
+ if (fputs(newline, fp) < 0)
+ return -1;
+ count -= p + 1 - buf;
+ buf = p + 1;
+ }
+ return 0;
+}
+
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
struct diff_words_data *diff_words = priv;
+ struct diff_words_style *style = diff_words->style;
int minus_first, minus_len, plus_first, plus_len;
const char *minus_begin, *minus_end, *plus_begin, *plus_end;
plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
if (diff_words->current_plus != plus_begin)
- fwrite(diff_words->current_plus,
- plus_begin - diff_words->current_plus, 1,
- diff_words->file);
+ fn_out_diff_words_write_helper(diff_words->file,
+ &style->ctx, style->newline,
+ plus_begin - diff_words->current_plus,
+ diff_words->current_plus);
if (minus_begin != minus_end)
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_OLD),
+ fn_out_diff_words_write_helper(diff_words->file,
+ &style->old, style->newline,
minus_end - minus_begin, minus_begin);
if (plus_begin != plus_end)
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_NEW),
+ fn_out_diff_words_write_helper(diff_words->file,
+ &style->new, style->newline,
plus_end - plus_begin, plus_begin);
diff_words->current_plus = plus_end;
xpparam_t xpp;
xdemitconf_t xecfg;
mmfile_t minus, plus;
+ struct diff_words_style *style = diff_words->style;
/* special case: only removal */
if (!diff_words->plus.text.size) {
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_OLD),
+ fn_out_diff_words_write_helper(diff_words->file,
+ &style->old, style->newline,
diff_words->minus.text.size, diff_words->minus.text.ptr);
diff_words->minus.text.size = 0;
return;
memset(&xecfg, 0, sizeof(xecfg));
diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
/* as only the hunk header will be parsed, we need a 0-context */
xecfg.ctxlen = 0;
xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
free(plus.ptr);
if (diff_words->current_plus != diff_words->plus.text.ptr +
diff_words->plus.text.size)
- fwrite(diff_words->current_plus,
+ fn_out_diff_words_write_helper(diff_words->file,
+ &style->ctx, style->newline,
diff_words->plus.text.ptr + diff_words->plus.text.size
- - diff_words->current_plus, 1,
- diff_words->file);
+ - diff_words->current_plus, diff_words->current_plus);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
}
if (len < 1) {
emit_line(ecbdata->file, reset, reset, line, len);
+ if (ecbdata->diff_words
+ && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
+ fputs("~\n", ecbdata->file);
return;
}
return;
}
diff_words_flush(ecbdata);
- line++;
- len--;
- emit_line(ecbdata->file, plain, reset, line, len);
+ if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
+ emit_line(ecbdata->file, plain, reset, line, len);
+ fputs("~\n", ecbdata->file);
+ } else {
+ /* don't print the prefix character */
+ emit_line(ecbdata->file, plain, reset, line+1, len-1);
+ }
return;
}
if (lbl[0][0] == '/') {
/* /dev/null */
strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
}
else if (lbl[1][0] == '/') {
strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
}
else {
if (one->mode != two->mode) {
strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
}
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
/*
* we do not run diff between different kind
check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.file = o->file;
ecbdata.header = header.len ? &header : NULL;
- xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+ xpp.flags = o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!prefixcmp(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
+ if (o->word_diff) {
+ int i;
+
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
+ ecbdata.diff_words->type = o->word_diff;
if (!o->word_regex)
o->word_regex = userdiff_word_regex(one);
if (!o->word_regex)
die ("Invalid regular expression: %s",
o->word_regex);
}
+ for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
+ if (o->word_diff == diff_words_styles[i].type) {
+ ecbdata.diff_words->style =
+ &diff_words_styles[i];
+ break;
+ }
+ }
+ if (DIFF_OPT_TST(o, COLOR_DIFF)) {
+ struct diff_words_style *st = ecbdata.diff_words->style;
+ st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
+ st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
+ st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
+ }
}
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
&xpp, &xecfg);
- if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+ if (o->word_diff)
free_diff_words_data(&ecbdata);
if (textconv_one)
free(mf1.ptr);
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
- xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+ xpp.flags = o->xdl_opts;
xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
&xpp, &xecfg);
}
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
&xpp, &xecfg);
struct diff_filespec *one,
struct diff_filespec *two,
struct diff_options *o,
- struct diff_filepair *p)
+ struct diff_filepair *p,
+ int use_color)
{
+ const char *set = diff_get_color(use_color, DIFF_METAINFO);
+ const char *reset = diff_get_color(use_color, DIFF_RESET);
+
strbuf_init(msg, PATH_MAX * 2 + 300);
switch (p->status) {
case DIFF_STATUS_COPIED:
- strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(msg, "\ncopy from ");
+ strbuf_addf(msg, "%ssimilarity index %d%%",
+ set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%scopy from ", reset, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addstr(msg, "\ncopy to ");
+ strbuf_addf(msg, "%s\n%scopy to ", reset, set);
quote_c_style(other, msg, NULL, 0);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_RENAMED:
- strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(msg, "\nrename from ");
+ strbuf_addf(msg, "%ssimilarity index %d%%",
+ set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%srename from ", reset, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addstr(msg, "\nrename to ");
+ strbuf_addf(msg, "%s\n%srename to ", reset, set);
quote_c_style(other, msg, NULL, 0);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
- strbuf_addf(msg, "dissimilarity index %d%%\n",
- similarity_index(p));
+ strbuf_addf(msg, "%sdissimilarity index %d%%%s\n",
+ set, similarity_index(p), reset);
break;
}
/* fallthru */
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
- strbuf_addf(msg, "index %.*s..%.*s",
+ strbuf_addf(msg, "%sindex %.*s..%.*s", set,
abbrev, sha1_to_hex(one->sha1),
abbrev, sha1_to_hex(two->sha1));
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
}
- if (msg->len)
- strbuf_setlen(msg, msg->len - 1);
}
static void run_diff_cmd(const char *pgm,
const char *xfrm_msg = NULL;
int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
- if (msg) {
- fill_metainfo(msg, name, other, one, two, o, p);
- xfrm_msg = msg->len ? msg->buf : NULL;
- }
-
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
else {
pgm = drv->external;
}
+ if (msg) {
+ /*
+ * don't use colors when the header is intended for an
+ * external diff driver
+ */
+ fill_metainfo(msg, name, other, one, two, o, p,
+ DIFF_OPT_TST(o, COLOR_DIFF) && !pgm);
+ xfrm_msg = msg->len ? msg->buf : NULL;
+ }
+
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
complete_rewrite);
void diff_setup(struct diff_options *options)
{
memset(options, 0, sizeof(*options));
+ memset(&diff_queued_diff, 0, sizeof(diff_queued_diff));
options->file = stdout;
const char *arg = av[0];
/* Output format options */
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
options->output_format |= DIFF_FORMAT_PATCH;
else if (opt_arg(arg, 'U', "unified", &options->context))
options->output_format |= DIFF_FORMAT_PATCH;
DIFF_OPT_CLR(options, COLOR_DIFF);
else if (!strcmp(arg, "--color-words")) {
DIFF_OPT_SET(options, COLOR_DIFF);
- DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+ options->word_diff = DIFF_WORDS_COLOR;
}
else if (!prefixcmp(arg, "--color-words=")) {
DIFF_OPT_SET(options, COLOR_DIFF);
- DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+ options->word_diff = DIFF_WORDS_COLOR;
options->word_regex = arg + 14;
}
+ else if (!strcmp(arg, "--word-diff")) {
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ }
+ else if (!prefixcmp(arg, "--word-diff=")) {
+ const char *type = arg + 12;
+ if (!strcmp(type, "plain"))
+ options->word_diff = DIFF_WORDS_PLAIN;
+ else if (!strcmp(type, "color")) {
+ DIFF_OPT_SET(options, COLOR_DIFF);
+ options->word_diff = DIFF_WORDS_COLOR;
+ }
+ else if (!strcmp(type, "porcelain"))
+ options->word_diff = DIFF_WORDS_PORCELAIN;
+ else if (!strcmp(type, "none"))
+ options->word_diff = DIFF_WORDS_NONE;
+ else
+ die("bad --word-diff argument: %s", type);
+ }
+ else if (!prefixcmp(arg, "--word-diff-regex=")) {
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ options->word_regex = arg + 18;
+ }
else if (!strcmp(arg, "--exit-code"))
DIFF_OPT_SET(options, EXIT_WITH_STATUS);
else if (!strcmp(arg, "--quiet"))
len2, p->two->path);
git_SHA1_Update(&ctx, buffer, len1);
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
diff_free_filepair(q->queue[i]);
free(q->queue);
- q->queue = NULL;
- q->nr = q->alloc = 0;
+ DIFF_QUEUE_CLEAR(q);
return result;
}
diff_free_filepair(q->queue[i]);
free_queue:
free(q->queue);
- q->queue = NULL;
- q->nr = q->alloc = 0;
+ DIFF_QUEUE_CLEAR(q);
if (options->close_file)
fclose(options->file);
int i;
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
if (!filter)
return;
int i;
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
void diffcore_std(struct diff_options *options)
{
+ /* We never run this function more than one time, because the
+ * rename/copy detection logic can only run once.
+ */
+ if (diff_queued_diff.run)
+ return;
+
if (options->skip_stat_unmatch)
diffcore_skip_stat_unmatch(options);
if (options->break_opt != -1)
DIFF_OPT_SET(options, HAS_CHANGES);
else
DIFF_OPT_CLR(options, HAS_CHANGES);
+
+ diff_queued_diff.run = 1;
}
int diff_result_code(struct diff_options *opt, int status)