static int diff_suppress_blank_empty;
static int diff_use_color_default = -1;
static int diff_color_moved_default;
+static int diff_color_moved_ws_default;
static int diff_context_default = 3;
static int diff_interhunk_context_default;
static const char *diff_word_regex_cfg;
GIT_COLOR_BOLD_YELLOW, /* NEW_MOVED ALTERNATIVE */
GIT_COLOR_FAINT, /* NEW_MOVED_DIM */
GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
+ GIT_COLOR_FAINT, /* CONTEXT_DIM */
+ GIT_COLOR_FAINT_RED, /* OLD_DIM */
+ GIT_COLOR_FAINT_GREEN, /* NEW_DIM */
+ GIT_COLOR_BOLD, /* CONTEXT_BOLD */
+ GIT_COLOR_BOLD_RED, /* OLD_BOLD */
+ GIT_COLOR_BOLD_GREEN, /* NEW_BOLD */
};
static const char *color_diff_slots[] = {
[DIFF_FILE_NEW_MOVED_ALT] = "newMovedAlternative",
[DIFF_FILE_NEW_MOVED_DIM] = "newMovedDimmed",
[DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed",
+ [DIFF_CONTEXT_DIM] = "contextDimmed",
+ [DIFF_FILE_OLD_DIM] = "oldDimmed",
+ [DIFF_FILE_NEW_DIM] = "newDimmed",
+ [DIFF_CONTEXT_BOLD] = "contextBold",
+ [DIFF_FILE_OLD_BOLD] = "oldBold",
+ [DIFF_FILE_NEW_BOLD] = "newBold",
};
static NORETURN void die_want_option(const char *option_name)
return COLOR_MOVED_NO;
else if (!strcmp(arg, "plain"))
return COLOR_MOVED_PLAIN;
+ else if (!strcmp(arg, "blocks"))
+ return COLOR_MOVED_BLOCKS;
else if (!strcmp(arg, "zebra"))
return COLOR_MOVED_ZEBRA;
else if (!strcmp(arg, "default"))
return COLOR_MOVED_DEFAULT;
+ else if (!strcmp(arg, "dimmed-zebra"))
+ return COLOR_MOVED_ZEBRA_DIM;
else if (!strcmp(arg, "dimmed_zebra"))
return COLOR_MOVED_ZEBRA_DIM;
else
- return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
+ return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
+}
+
+static int parse_color_moved_ws(const char *arg)
+{
+ int ret = 0;
+ struct string_list l = STRING_LIST_INIT_DUP;
+ struct string_list_item *i;
+
+ string_list_split(&l, arg, ',', -1);
+
+ for_each_string_list_item(i, &l) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, i->string);
+ strbuf_trim(&sb);
+
+ if (!strcmp(sb.buf, "ignore-space-change"))
+ ret |= XDF_IGNORE_WHITESPACE_CHANGE;
+ else if (!strcmp(sb.buf, "ignore-space-at-eol"))
+ ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ else if (!strcmp(sb.buf, "ignore-all-space"))
+ ret |= XDF_IGNORE_WHITESPACE;
+ else if (!strcmp(sb.buf, "allow-indentation-change"))
+ ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
+ else
+ error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
+
+ strbuf_release(&sb);
+ }
+
+ if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
+ (ret & XDF_WHITESPACE_FLAGS))
+ die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
+
+ string_list_clear(&l, 0);
+
+ return ret;
}
int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_color_moved_default = cm;
return 0;
}
+ if (!strcmp(var, "diff.colormovedws")) {
+ int cm = parse_color_moved_ws(value);
+ if (cm < 0)
+ return -1;
+ diff_color_moved_ws_default = cm;
+ return 0;
+ }
if (!strcmp(var, "diff.context")) {
diff_context_default = git_config_int(var, value);
if (diff_context_default < 0)
return count;
}
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+static int fill_mmfile(struct repository *r, mmfile_t *mf,
+ struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
mf->ptr = (char *)""; /* does not matter */
mf->size = 0;
return 0;
}
- else if (diff_populate_filespec(one, 0))
+ else if (diff_populate_filespec(r, one, 0))
return -1;
mf->ptr = one->data;
}
/* like fill_mmfile, but only for size, so we can avoid retrieving blob */
-static unsigned long diff_filespec_size(struct diff_filespec *one)
+static unsigned long diff_filespec_size(struct repository *r,
+ struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return 0;
- diff_populate_filespec(one, CHECK_SIZE_ONLY);
+ diff_populate_filespec(r, one, CHECK_SIZE_ONLY);
return one->size;
}
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
}
-static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
+static void emit_line_0(struct diff_options *o,
+ const char *set, unsigned reverse, const char *reset,
int first, const char *line, int len)
{
int has_trailing_newline, has_trailing_carriage_return;
int nofirst;
FILE *file = o->file;
- fputs(diff_line_prefix(o), file);
+ if (first)
+ fputs(diff_line_prefix(o), file);
+ else if (!len)
+ return;
if (len == 0) {
has_trailing_newline = (first == '\n');
}
if (len || !nofirst) {
+ if (reverse && want_color(o->use_color))
+ fputs(GIT_COLOR_REVERSE, file);
fputs(set, file);
- if (!nofirst)
+ if (first && !nofirst)
fputc(first, file);
fwrite(line, len, 1, file);
fputs(reset, file);
static void emit_line(struct diff_options *o, const char *set, const char *reset,
const char *line, int len)
{
- emit_line_0(o, set, reset, line[0], line+1, len-1);
+ emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
}
enum diff_symbol {
struct hashmap_entry ent;
const struct emitted_diff_symbol *es;
struct moved_entry *next_line;
+ struct ws_delta *wsd;
+};
+
+/**
+ * The struct ws_delta holds white space differences between moved lines, i.e.
+ * between '+' and '-' lines that have been detected to be a move.
+ * The string contains the difference in leading white spaces, before the
+ * rest of the line is compared using the white space config for move
+ * coloring. The current_longer indicates if the first string in the
+ * comparision is longer than the second.
+ */
+struct ws_delta {
+ char *string;
+ unsigned int current_longer : 1;
};
+#define WS_DELTA_INIT { NULL, 0 }
+
+static int compute_ws_delta(const struct emitted_diff_symbol *a,
+ const struct emitted_diff_symbol *b,
+ struct ws_delta *out)
+{
+ const struct emitted_diff_symbol *longer = a->len > b->len ? a : b;
+ const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
+ int d = longer->len - shorter->len;
+
+ out->string = xmemdupz(longer->line, d);
+ out->current_longer = (a == longer);
+
+ return !strncmp(longer->line + d, shorter->line, shorter->len);
+}
+
+static int cmp_in_block_with_wsd(const struct diff_options *o,
+ const struct moved_entry *cur,
+ const struct moved_entry *match,
+ struct moved_entry *pmb,
+ int n)
+{
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ int al = cur->es->len, cl = l->len;
+ const char *a = cur->es->line,
+ *b = match->es->line,
+ *c = l->line;
+
+ int wslen;
+
+ /*
+ * We need to check if 'cur' is equal to 'match'.
+ * As those are from the same (+/-) side, we do not need to adjust for
+ * indent changes. However these were found using fuzzy matching
+ * so we do have to check if they are equal.
+ */
+ if (strcmp(a, b))
+ return 1;
+
+ if (!pmb->wsd)
+ /*
+ * No white space delta was carried forward? This can happen
+ * when we exit early in this function and do not carry
+ * forward ws.
+ */
+ return 1;
+
+ /*
+ * The indent changes of the block are known and carried forward in
+ * pmb->wsd; however we need to check if the indent changes of the
+ * current line are still the same as before.
+ *
+ * To do so we need to compare 'l' to 'cur', adjusting the
+ * one of them for the white spaces, depending which was longer.
+ */
+
+ wslen = strlen(pmb->wsd->string);
+ if (pmb->wsd->current_longer) {
+ c += wslen;
+ cl -= wslen;
+ } else {
+ a += wslen;
+ al -= wslen;
+ }
-static int moved_entry_cmp(const struct diff_options *diffopt,
- const struct moved_entry *a,
- const struct moved_entry *b,
+ if (strcmp(a, c))
+ return 1;
+
+ return 0;
+}
+
+static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
+ const void *entry,
+ const void *entry_or_key,
const void *keydata)
{
+ const struct diff_options *diffopt = hashmap_cmp_fn_data;
+ const struct moved_entry *a = entry;
+ const struct moved_entry *b = entry_or_key;
+ unsigned flags = diffopt->color_moved_ws_handling
+ & XDF_WHITESPACE_FLAGS;
+
+ if (diffopt->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ /*
+ * As there is not specific white space config given,
+ * we'd need to check for a new block, so ignore all
+ * white space. The setup of the white space
+ * configuration for the next block is done else where
+ */
+ flags |= XDF_IGNORE_WHITESPACE;
+
return !xdiff_compare_lines(a->es->line, a->es->len,
b->es->line, b->es->len,
- diffopt->xdl_opts);
+ flags);
}
static struct moved_entry *prepare_entry(struct diff_options *o,
{
struct moved_entry *ret = xmalloc(sizeof(*ret));
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
+ unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
- ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
+ ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
ret->es = l;
ret->next_line = NULL;
+ ret->wsd = NULL;
return ret;
}
}
}
+static void pmb_advance_or_null(struct diff_options *o,
+ struct moved_entry *match,
+ struct hashmap *hm,
+ struct moved_entry **pmb,
+ int pmb_nr)
+{
+ int i;
+ for (i = 0; i < pmb_nr; i++) {
+ struct moved_entry *prev = pmb[i];
+ struct moved_entry *cur = (prev && prev->next_line) ?
+ prev->next_line : NULL;
+ if (cur && !hm->cmpfn(o, cur, match, NULL)) {
+ pmb[i] = cur;
+ } else {
+ pmb[i] = NULL;
+ }
+ }
+}
+
+static void pmb_advance_or_null_multi_match(struct diff_options *o,
+ struct moved_entry *match,
+ struct hashmap *hm,
+ struct moved_entry **pmb,
+ int pmb_nr, int n)
+{
+ int i;
+ char *got_match = xcalloc(1, pmb_nr);
+
+ for (; match; match = hashmap_get_next(hm, match)) {
+ for (i = 0; i < pmb_nr; i++) {
+ struct moved_entry *prev = pmb[i];
+ struct moved_entry *cur = (prev && prev->next_line) ?
+ prev->next_line : NULL;
+ if (!cur)
+ continue;
+ if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
+ got_match[i] |= 1;
+ }
+ }
+
+ for (i = 0; i < pmb_nr; i++) {
+ if (got_match[i]) {
+ /* Carry the white space delta forward */
+ pmb[i]->next_line->wsd = pmb[i]->wsd;
+ pmb[i] = pmb[i]->next_line;
+ } else
+ pmb[i] = NULL;
+ }
+}
+
static int shrink_potential_moved_blocks(struct moved_entry **pmb,
int pmb_nr)
{
if (lp < pmb_nr && rp > -1 && lp < rp) {
pmb[lp] = pmb[rp];
+ if (pmb[rp]->wsd) {
+ free(pmb[rp]->wsd->string);
+ FREE_AND_NULL(pmb[rp]->wsd);
+ }
pmb[rp] = NULL;
rp--;
lp++;
struct moved_entry *key;
struct moved_entry *match = NULL;
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
- int i;
switch (l->s) {
case DIFF_SYMBOL_PLUS:
hm = del_lines;
key = prepare_entry(o, n);
- match = hashmap_get(hm, key, o);
+ match = hashmap_get(hm, key, NULL);
free(key);
break;
case DIFF_SYMBOL_MINUS:
hm = add_lines;
key = prepare_entry(o, n);
- match = hashmap_get(hm, key, o);
+ match = hashmap_get(hm, key, NULL);
free(key);
break;
default:
if (o->color_moved == COLOR_MOVED_PLAIN)
continue;
- /* Check any potential block runs, advance each or nullify */
- for (i = 0; i < pmb_nr; i++) {
- struct moved_entry *p = pmb[i];
- struct moved_entry *pnext = (p && p->next_line) ?
- p->next_line : NULL;
- if (pnext && !hm->cmpfn(o, pnext, match, NULL)) {
- pmb[i] = p->next_line;
- } else {
- pmb[i] = NULL;
- }
- }
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
+ else
+ pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
*/
for (; match; match = hashmap_get_next(hm, match)) {
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
- pmb[pmb_nr++] = match;
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
+ struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
+ if (compute_ws_delta(l, match->es, wsd)) {
+ match->wsd = wsd;
+ pmb[pmb_nr++] = match;
+ } else
+ free(wsd);
+ } else {
+ pmb[pmb_nr++] = match;
+ }
}
flipped_block = (flipped_block + 1) % 2;
block_length++;
- if (flipped_block)
+ if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
}
adjust_last_block(o, n, block_length);
static void emit_line_ws_markup(struct diff_options *o,
const char *set, const char *reset,
- const char *line, int len, char sign,
+ const char *line, int len,
+ const char *set_sign, char sign,
unsigned ws_rule, int blank_at_eof)
{
const char *ws = NULL;
ws = NULL;
}
- if (!ws)
- emit_line_0(o, set, reset, sign, line, len);
- else if (blank_at_eof)
+ if (!ws && !set_sign)
+ emit_line_0(o, set, 0, reset, sign, line, len);
+ else if (!ws) {
+ /* Emit just the prefix, then the rest. */
+ emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
+ sign, "", 0);
+ emit_line_0(o, set, 0, reset, 0, line, len);
+ } else if (blank_at_eof)
/* Blank line at EOF - paint '+' as well */
- emit_line_0(o, ws, reset, sign, line, len);
+ emit_line_0(o, ws, 0, reset, sign, line, len);
else {
/* Emit just the prefix, then the rest. */
- emit_line_0(o, set, reset, sign, "", 0);
+ emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
+ sign, "", 0);
ws_check_emit(line, len, ws_rule,
o->file, set, reset, ws);
}
struct emitted_diff_symbol *eds)
{
static const char *nneof = " No newline at end of file\n";
- const char *context, *reset, *set, *meta, *fraginfo;
+ const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
struct strbuf sb = STRBUF_INIT;
enum diff_symbol s = eds->s;
context = diff_get_color_opt(o, DIFF_CONTEXT);
reset = diff_get_color_opt(o, DIFF_RESET);
putc('\n', o->file);
- emit_line_0(o, context, reset, '\\',
+ emit_line_0(o, context, 0, reset, '\\',
nneof, strlen(nneof));
break;
case DIFF_SYMBOL_SUBMODULE_HEADER:
case DIFF_SYMBOL_CONTEXT:
set = diff_get_color_opt(o, DIFF_CONTEXT);
reset = diff_get_color_opt(o, DIFF_RESET);
- emit_line_ws_markup(o, set, reset, line, len, ' ',
+ set_sign = NULL;
+ if (o->flags.dual_color_diffed_diffs) {
+ char c = !len ? 0 : line[0];
+
+ if (c == '+')
+ set = diff_get_color_opt(o, DIFF_FILE_NEW);
+ else if (c == '@')
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
+ else if (c == '-')
+ set = diff_get_color_opt(o, DIFF_FILE_OLD);
+ }
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
break;
case DIFF_SYMBOL_PLUS:
set = diff_get_color_opt(o, DIFF_FILE_NEW);
}
reset = diff_get_color_opt(o, DIFF_RESET);
- emit_line_ws_markup(o, set, reset, line, len, '+',
+ if (!o->flags.dual_color_diffed_diffs)
+ set_sign = NULL;
+ else {
+ char c = !len ? 0 : line[0];
+
+ set_sign = set;
+ if (c == '-')
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_BOLD);
+ else if (c == '@')
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
+ else if (c == '+')
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_BOLD);
+ else
+ set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD);
+ flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
+ }
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
break;
set = diff_get_color_opt(o, DIFF_FILE_OLD);
}
reset = diff_get_color_opt(o, DIFF_RESET);
- emit_line_ws_markup(o, set, reset, line, len, '-',
+ if (!o->flags.dual_color_diffed_diffs)
+ set_sign = NULL;
+ else {
+ char c = !len ? 0 : line[0];
+
+ set_sign = set;
+ if (c == '+')
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_DIM);
+ else if (c == '@')
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
+ else if (c == '-')
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_DIM);
+ else
+ set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
+ }
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
break;
case DIFF_SYMBOL_WORDS_PORCELAIN:
const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+ const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
static const char atat[2] = { '@', '@' };
const char *cp, *ep;
struct strbuf msgbuf = STRBUF_INIT;
ep += 2; /* skip over @@ */
/* The hunk header in fraginfo color */
+ if (ecbdata->opt->flags.dual_color_diffed_diffs)
+ strbuf_addstr(&msgbuf, reverse);
strbuf_addstr(&msgbuf, frag);
strbuf_add(&msgbuf, line, ep - line);
strbuf_addstr(&msgbuf, reset);
if (regcomp(ecbdata->diff_words->word_regex,
o->word_regex,
REG_EXTENDED | REG_NEWLINE))
- die ("Invalid regular expression: %s",
- 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) {
}
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
- diff_populate_filespec(p->one, 0);
- diff_populate_filespec(p->two, 0);
- diffcore_count_changes(p->one, p->two, NULL, NULL,
+ diff_populate_filespec(options->repo, p->one, 0);
+ diff_populate_filespec(options->repo, p->two, 0);
+ diffcore_count_changes(options->repo,
+ p->one, p->two, NULL, NULL,
&copied, &added);
diff_free_filespec_data(p->one);
diff_free_filespec_data(p->two);
} else if (DIFF_FILE_VALID(p->one)) {
- diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
+ diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY);
copied = added = 0;
diff_free_filespec_data(p->one);
} else if (DIFF_FILE_VALID(p->two)) {
- diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
+ diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY);
copied = 0;
added = p->two->size;
diff_free_filespec_data(p->two);
emit_binary_diff_body(o, two, one);
}
-int diff_filespec_is_binary(struct diff_filespec *one)
+int diff_filespec_is_binary(struct repository *r,
+ struct diff_filespec *one)
{
if (one->is_binary == -1) {
diff_filespec_load_driver(one);
one->is_binary = one->driver->binary;
else {
if (!one->data && DIFF_FILE_VALID(one))
- diff_populate_filespec(one, CHECK_BINARY);
+ diff_populate_filespec(r, one, CHECK_BINARY);
if (one->is_binary == -1 && one->data)
one->is_binary = buffer_is_binary(one->data,
one->size);
if ((one->mode ^ two->mode) & S_IFMT)
goto free_ab_and_return;
if (complete_rewrite &&
- (textconv_one || !diff_filespec_is_binary(one)) &&
- (textconv_two || !diff_filespec_is_binary(two))) {
+ (textconv_one || !diff_filespec_is_binary(o->repo, one)) &&
+ (textconv_two || !diff_filespec_is_binary(o->repo, two))) {
emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
header.buf, header.len, 0);
strbuf_reset(&header);
emit_rewrite_diff(name_a, name_b, one, two,
- textconv_one, textconv_two, o);
+ textconv_one, textconv_two, o);
o->found_changes = 1;
goto free_ab_and_return;
}
strbuf_reset(&header);
goto free_ab_and_return;
} else if (!o->flags.text &&
- ( (!textconv_one && diff_filespec_is_binary(one)) ||
- (!textconv_two && diff_filespec_is_binary(two)) )) {
+ ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) ||
+ (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) {
struct strbuf sb = STRBUF_INIT;
if (!one->data && !two->data &&
S_ISREG(one->mode) && S_ISREG(two->mode) &&
strbuf_release(&sb);
goto free_ab_and_return;
}
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
/* Quite common confusing case */
if (mf1.size == mf2.size &&
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
+ if (o->flags.suppress_diff_headers)
+ lbl[0] = NULL;
ecbdata.label_path = lbl;
ecbdata.color_diff = want_color(o->use_color);
ecbdata.ws_rule = whitespace_rule(name_b);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.opt = o;
- ecbdata.header = header.len ? &header : NULL;
+ if (header.len && !o->flags.suppress_diff_headers)
+ ecbdata.header = &header;
xpp.flags = o->xdl_opts;
xpp.anchors = o->anchors;
xpp.anchors_nr = o->anchors_nr;
same_contents = !oidcmp(&one->oid, &two->oid);
- if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
+ if (diff_filespec_is_binary(o->repo, one) ||
+ diff_filespec_is_binary(o->repo, two)) {
data->is_binary = 1;
if (same_contents) {
data->added = 0;
data->deleted = 0;
} else {
- data->added = diff_filespec_size(two);
- data->deleted = diff_filespec_size(one);
+ data->added = diff_filespec_size(o->repo, two);
+ data->deleted = diff_filespec_size(o->repo, one);
}
}
else if (complete_rewrite) {
- diff_populate_filespec(one, 0);
- diff_populate_filespec(two, 0);
+ diff_populate_filespec(o->repo, one, 0);
+ diff_populate_filespec(o->repo, two, 0);
data->deleted = count_lines(one->data, one->size);
data->added = count_lines(two->data, two->size);
}
xpparam_t xpp;
xdemitconf_t xecfg;
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
memset(&xpp, 0, sizeof(xpp));
data.ws_rule = whitespace_rule(attr_path);
data.conflict_marker_size = ll_merge_marker_size(attr_path);
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
/*
* introduced changes, and as long as the "new" side is text, we
* can and should check what it introduces.
*/
- if (diff_filespec_is_binary(two))
+ if (diff_filespec_is_binary(o->repo, two))
goto free_and_return;
else {
/* Crazy xdl interfaces.. */
* the work tree has that object contents, return true, so that
* prepare_temp_file() does not have to inflate and extract.
*/
-static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
+static int reuse_worktree_file(struct index_state *istate,
+ const char *name,
+ const struct object_id *oid,
+ int want_file)
{
const struct cache_entry *ce;
struct stat st;
* by diff-cache --cached, which does read the cache before
* calling us.
*/
- if (!active_cache)
+ if (!istate->cache)
return 0;
/* We want to avoid the working directory if our caller
* Similarly, if we'd have to convert the file contents anyway, that
* makes the optimization not worthwhile.
*/
- if (!want_file && would_convert_to_git(&the_index, name))
+ if (!want_file && would_convert_to_git(istate, name))
return 0;
len = strlen(name);
- pos = cache_name_pos(name, len);
+ pos = index_name_pos(istate, name, len);
if (pos < 0)
return 0;
- ce = active_cache[pos];
+ ce = istate->cache[pos];
/*
* This is not the sha1 we are looking for, or
* If ce matches the file in the work tree, we can reuse it.
*/
if (ce_uptodate(ce) ||
- (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+ (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0)))
return 1;
return 0;
* grab the data for the blob (or file) for our own in-core comparison.
* diff_filespec has data and size fields for this purpose.
*/
-int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
+int diff_populate_filespec(struct repository *r,
+ struct diff_filespec *s,
+ unsigned int flags)
{
int size_only = flags & CHECK_SIZE_ONLY;
int err = 0;
return diff_populate_gitlink(s, size_only);
if (!s->oid_valid ||
- reuse_worktree_file(s->path, &s->oid, 0)) {
+ reuse_worktree_file(r->index, s->path, &s->oid, 0)) {
struct strbuf buf = STRBUF_INIT;
struct stat st;
int fd;
* point if the path requires us to run the content
* conversion.
*/
- if (size_only && !would_convert_to_git(&the_index, s->path))
+ if (size_only && !would_convert_to_git(r->index, s->path))
return 0;
/*
/*
* Convert from working tree format to canonical git format
*/
- if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
+ if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) {
size_t size = 0;
munmap(s->data, s->size);
s->should_munmap = 0;
else {
enum object_type type;
if (size_only || (flags & CHECK_BINARY)) {
- type = oid_object_info(the_repository, &s->oid,
- &s->size);
+ type = oid_object_info(r, &s->oid, &s->size);
if (type < 0)
die("unable to read %s",
oid_to_hex(&s->oid));
FREE_AND_NULL(s->cnt_data);
}
-static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
+static void prep_temp_blob(struct index_state *istate,
+ const char *path, struct diff_tempfile *temp,
void *blob,
unsigned long size,
const struct object_id *oid,
temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
if (!temp->tempfile)
die_errno("unable to create temp-file");
- if (convert_to_working_tree(path,
+ if (convert_to_working_tree(istate, path,
(const char *)blob, (size_t)size, &buf)) {
blob = buf.buf;
size = buf.len;
free(path_dup);
}
-static struct diff_tempfile *prepare_temp_file(const char *name,
- struct diff_filespec *one)
+static struct diff_tempfile *prepare_temp_file(struct repository *r,
+ const char *name,
+ struct diff_filespec *one)
{
struct diff_tempfile *temp = claim_diff_tempfile();
if (!S_ISGITLINK(one->mode) &&
(!one->oid_valid ||
- reuse_worktree_file(name, &one->oid, 1))) {
+ reuse_worktree_file(r->index, name, &one->oid, 1))) {
struct stat st;
if (lstat(name, &st) < 0) {
if (errno == ENOENT)
struct strbuf sb = STRBUF_INIT;
if (strbuf_readlink(&sb, name, st.st_size) < 0)
die_errno("readlink(%s)", name);
- prep_temp_blob(name, temp, sb.buf, sb.len,
+ prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
(one->oid_valid ?
&one->oid : &null_oid),
(one->oid_valid ?
return temp;
}
else {
- if (diff_populate_filespec(one, 0))
+ if (diff_populate_filespec(r, one, 0))
die("cannot read data blob for %s", one->path);
- prep_temp_blob(name, temp, one->data, one->size,
+ prep_temp_blob(r->index, name, temp,
+ one->data, one->size,
&one->oid, one->mode);
}
return temp;
}
-static void add_external_diff_name(struct argv_array *argv,
+static void add_external_diff_name(struct repository *r,
+ struct argv_array *argv,
const char *name,
struct diff_filespec *df)
{
- struct diff_tempfile *temp = prepare_temp_file(name, df);
+ struct diff_tempfile *temp = prepare_temp_file(r, name, df);
argv_array_push(argv, temp->name);
argv_array_push(argv, temp->hex);
argv_array_push(argv, temp->mode);
argv_array_push(&argv, name);
if (one && two) {
- add_external_diff_name(&argv, name, one);
+ add_external_diff_name(o->repo, &argv, name, one);
if (!other)
- add_external_diff_name(&argv, name, two);
+ add_external_diff_name(o->repo, &argv, name, two);
else {
- add_external_diff_name(&argv, other, two);
+ add_external_diff_name(o->repo, &argv, other, two);
argv_array_push(&argv, other);
argv_array_push(&argv, xfrm_msg);
}
char *hex = oid_to_hex(oid);
if (abbrev < 0)
abbrev = FALLBACK_DEFAULT_ABBREV;
- if (abbrev > GIT_SHA1_HEXSZ)
+ if (abbrev > the_hash_algo->hexsz)
BUG("oid abbreviation out of range: %d", abbrev);
if (abbrev)
hex[abbrev] = '\0';
if (o->flags.binary) {
mmfile_t mf;
- if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
- (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
+ if ((!fill_mmfile(o->repo, &mf, one) &&
+ diff_filespec_is_binary(o->repo, one)) ||
+ (!fill_mmfile(o->repo, &mf, two) &&
+ diff_filespec_is_binary(o->repo, two)))
abbrev = hexsz;
}
strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
*/
struct diff_filespec *null = alloc_filespec(two->path);
run_diff_cmd(NULL, name, other, attr_path,
- one, null, &msg, o, p);
+ one, null, &msg,
+ o, p);
free(null);
strbuf_release(&msg);
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p);
+ builtin_diffstat(p->one->path, NULL, NULL, NULL,
+ diffstat, o, p);
return;
}
diff_fill_oid_info(p->one);
diff_fill_oid_info(p->two);
- builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
+ builtin_diffstat(name, other, p->one, p->two,
+ diffstat, o, p);
}
static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
memcpy(options, &default_diff_options, sizeof(*options));
options->file = stdout;
+ options->repo->index = &the_index;
options->abbrev = DEFAULT_ABBREV;
options->line_termination = '\n';
}
options->color_moved = diff_color_moved_default;
+ options->color_moved_ws_handling = diff_color_moved_ws_default;
}
void diff_setup_done(struct diff_options *options)
if (options->detect_rename && options->rename_limit < 0)
options->rename_limit = diff_rename_limit_default;
- if (options->setup & DIFF_SETUP_USE_CACHE) {
- if (!active_cache)
- /* read-cache does not die even when it fails
- * so it is safe for us to do this here. Also
- * it does not smudge active_cache or active_nr
- * when it fails, so we do not have to worry about
- * cleaning it up ourselves either.
- */
- read_cache();
- }
if (hexsz < options->abbrev)
options->abbrev = hexsz; /* full */
if (cm < 0)
die("bad --color-moved argument: %s", arg);
options->color_moved = cm;
+ } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
+ options->color_moved_ws_handling = parse_color_moved_ws(arg);
} else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
const char *abbrev;
/* Do we want all 40 hex characters? */
- if (len == GIT_SHA1_HEXSZ)
+ if (len == the_hash_algo->hexsz)
return oid_to_hex(oid);
/* An abbreviated value is fine, possibly followed by an ellipsis. */
* the automatic sizing is supposed to give abblen that ensures
* uniqueness across all objects (statistically speaking).
*/
- if (abblen < GIT_SHA1_HEXSZ - 3) {
+ if (abblen < the_hash_algo->hexsz - 3) {
static char hex[GIT_MAX_HEXSZ + 1];
if (len < abblen && abblen <= len + 2)
xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
if (diff_header_only)
continue;
- if (fill_mmfile(&mf1, p->one) < 0 ||
- fill_mmfile(&mf2, p->two) < 0)
+ if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
+ fill_mmfile(options->repo, &mf2, p->two) < 0)
return error("unable to read files to diff");
- if (diff_filespec_is_binary(p->one) ||
- diff_filespec_is_binary(p->two)) {
+ if (diff_filespec_is_binary(options->repo, p->one) ||
+ diff_filespec_is_binary(options->repo, p->two)) {
git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
GIT_SHA1_HEXSZ);
git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
if (o->color_moved) {
struct hashmap add_lines, del_lines;
- hashmap_init(&del_lines,
- (hashmap_cmp_fn)moved_entry_cmp, o, 0);
- hashmap_init(&add_lines,
- (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
+
+ hashmap_init(&del_lines, moved_entry_cmp, o, 0);
+ hashmap_init(&add_lines, moved_entry_cmp, o, 0);
add_lines_to_move_detection(o, &add_lines, &del_lines);
mark_color_as_moved(o, &add_lines, &del_lines);
}
/* Check whether two filespecs with the same mode and size are identical */
-static int diff_filespec_is_identical(struct diff_filespec *one,
+static int diff_filespec_is_identical(struct repository *r,
+ struct diff_filespec *one,
struct diff_filespec *two)
{
if (S_ISGITLINK(one->mode))
return 0;
- if (diff_populate_filespec(one, 0))
+ if (diff_populate_filespec(r, one, 0))
return 0;
- if (diff_populate_filespec(two, 0))
+ if (diff_populate_filespec(r, two, 0))
return 0;
return !memcmp(one->data, two->data, one->size);
}
-static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+static int diff_filespec_check_stat_unmatch(struct repository *r,
+ struct diff_filepair *p)
{
if (p->done_skip_stat_unmatch)
return p->skip_stat_unmatch_result;
!DIFF_FILE_VALID(p->two) ||
(p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
- diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
- diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
+ diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) ||
+ diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
- !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
return p->skip_stat_unmatch_result;
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if (diff_filespec_check_stat_unmatch(p))
+ if (diff_filespec_check_stat_unmatch(diffopt->repo, p))
diff_q(&outq, p);
else {
/*
if (!options->found_follow) {
/* See try_to_follow_renames() in tree-diff.c */
if (options->break_opt != -1)
- diffcore_break(options->break_opt);
+ diffcore_break(options->repo,
+ options->break_opt);
if (options->detect_rename)
diffcore_rename(options);
if (options->break_opt != -1)
return;
if (options->flags.quick && options->skip_stat_unmatch &&
- !diff_filespec_check_stat_unmatch(p))
+ !diff_filespec_check_stat_unmatch(options->repo, p))
return;
options->flags.has_changes = 1;
return pair;
}
-static char *run_textconv(const char *pgm, struct diff_filespec *spec,
- size_t *outsize)
+static char *run_textconv(struct repository *r,
+ const char *pgm,
+ struct diff_filespec *spec,
+ size_t *outsize)
{
struct diff_tempfile *temp;
const char *argv[3];
struct strbuf buf = STRBUF_INIT;
int err = 0;
- temp = prepare_temp_file(spec->path, spec);
+ temp = prepare_temp_file(r, spec->path, spec);
*arg++ = pgm;
*arg++ = temp->name;
*arg = NULL;
struct diff_filespec *df,
char **outbuf)
{
+ struct repository *r = the_repository;
size_t size;
if (!driver) {
*outbuf = "";
return 0;
}
- if (diff_populate_filespec(df, 0))
+ if (diff_populate_filespec(r, df, 0))
die("unable to read files to diff");
*outbuf = df->data;
return df->size;
return size;
}
- *outbuf = run_textconv(driver->textconv, df, &size);
+ *outbuf = run_textconv(r, driver->textconv, df, &size);
if (!*outbuf)
die("unable to read files to diff");