#include "string-list.h"
#include "argv-array.h"
#include "graph.h"
+#include "packfile.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
GIT_COLOR_YELLOW, /* COMMIT */
GIT_COLOR_BG_RED, /* WHITESPACE */
GIT_COLOR_NORMAL, /* FUNCINFO */
- GIT_COLOR_MAGENTA, /* OLD_MOVED */
- GIT_COLOR_BLUE, /* OLD_MOVED ALTERNATIVE */
- GIT_COLOR_CYAN, /* NEW_MOVED */
- GIT_COLOR_YELLOW, /* NEW_MOVED ALTERNATIVE */
+ GIT_COLOR_BOLD_MAGENTA, /* OLD_MOVED */
+ GIT_COLOR_BOLD_BLUE, /* OLD_MOVED ALTERNATIVE */
+ GIT_COLOR_FAINT, /* OLD_MOVED_DIM */
+ GIT_COLOR_FAINT_ITALIC, /* OLD_MOVED_ALTERNATIVE_DIM */
+ GIT_COLOR_BOLD_CYAN, /* NEW_MOVED */
+ GIT_COLOR_BOLD_YELLOW, /* NEW_MOVED ALTERNATIVE */
+ GIT_COLOR_FAINT, /* NEW_MOVED_DIM */
+ GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
};
static NORETURN void die_want_option(const char *option_name)
return DIFF_FILE_OLD_MOVED;
if (!strcasecmp(var, "oldmovedalternative"))
return DIFF_FILE_OLD_MOVED_ALT;
+ if (!strcasecmp(var, "oldmoveddimmed"))
+ return DIFF_FILE_OLD_MOVED_DIM;
+ if (!strcasecmp(var, "oldmovedalternativedimmed"))
+ return DIFF_FILE_OLD_MOVED_ALT_DIM;
if (!strcasecmp(var, "newmoved"))
return DIFF_FILE_NEW_MOVED;
if (!strcasecmp(var, "newmovedalternative"))
return DIFF_FILE_NEW_MOVED_ALT;
+ if (!strcasecmp(var, "newmoveddimmed"))
+ return DIFF_FILE_NEW_MOVED_DIM;
+ if (!strcasecmp(var, "newmovedalternativedimmed"))
+ return DIFF_FILE_NEW_MOVED_ALT_DIM;
return -1;
}
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
- return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'plain'"));
+ return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
}
int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}
- if (starts_with(var, "submodule."))
- return parse_submodule_config_option(var, value);
-
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
* If this diff_tempfile instance refers to a temporary file,
* this tempfile object is used to manage its lifetime.
*/
- struct tempfile tempfile;
+ struct tempfile *tempfile;
} diff_temp[2];
-typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
-
struct emit_callback {
int color_diff;
unsigned ws_rule;
int blank_at_eof_in_postimage;
int lno_in_preimage;
int lno_in_postimage;
- sane_truncate_fn truncate;
const char **label_path;
struct diff_words_data *diff_words;
struct diff_options *opt;
#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16)
#define DIFF_SYMBOL_MOVED_LINE (1<<17)
#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18)
+#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19)
#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)
/*
struct moved_entry *next_line;
};
-static int next_byte(const char **cp, const char **endp,
- const struct diff_options *diffopt)
-{
- int retval;
-
- if (*cp > *endp)
- return -1;
-
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
- while (*cp < *endp && isspace(**cp))
- (*cp)++;
- /*
- * After skipping a couple of whitespaces, we still have to
- * account for one space.
- */
- return (int)' ';
- }
-
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
- while (*cp < *endp && isspace(**cp))
- (*cp)++;
- /* return the first non-ws character via the usual below */
- }
-
- retval = (unsigned char)(**cp);
- (*cp)++;
- return retval;
-}
-
static int moved_entry_cmp(const struct diff_options *diffopt,
const struct moved_entry *a,
const struct moved_entry *b,
const void *keydata)
{
- const char *ap = a->es->line, *ae = a->es->line + a->es->len;
- const char *bp = b->es->line, *be = b->es->line + b->es->len;
-
- if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
- return a->es->len != b->es->len || memcmp(ap, bp, a->es->len);
-
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
- while (ae > ap && isspace(*ae))
- ae--;
- while (be > bp && isspace(*be))
- be--;
- }
-
- while (1) {
- int ca, cb;
- ca = next_byte(&ap, &ae, diffopt);
- cb = next_byte(&bp, &be, diffopt);
- if (ca != cb)
- return 1;
- if (ca < 0)
- return 0;
- }
-}
-
-static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
-{
- if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
- static struct strbuf sb = STRBUF_INIT;
- const char *ap = es->line, *ae = es->line + es->len;
- int c;
-
- strbuf_reset(&sb);
- while (ae > ap && isspace(*ae))
- ae--;
- while ((c = next_byte(&ap, &ae, o)) > 0)
- strbuf_addch(&sb, c);
-
- return memhash(sb.buf, sb.len);
- } else {
- return memhash(es->line, es->len);
- }
+ return !xdiff_compare_lines(a->es->line, a->es->len,
+ b->es->line, b->es->len,
+ diffopt->xdl_opts);
}
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];
- ret->ent.hash = get_string_hash(l, o);
+ ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
ret->es = l;
ret->next_line = NULL;
return rp + 1;
}
+/*
+ * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing.
+ *
+ * Otherwise, if the last block has fewer alphanumeric characters than
+ * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in
+ * that block.
+ *
+ * The last block consists of the (n - block_length)'th line up to but not
+ * including the nth line.
+ *
+ * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
+ * Think of a way to unify them.
+ */
+static void adjust_last_block(struct diff_options *o, int n, int block_length)
+{
+ int i, alnum_count = 0;
+ if (o->color_moved == COLOR_MOVED_PLAIN)
+ return;
+ for (i = 1; i < block_length + 1; i++) {
+ const char *c = o->emitted_symbols->buf[n - i].line;
+ for (; *c; c++) {
+ if (!isalnum(*c))
+ continue;
+ alnum_count++;
+ if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
+ return;
+ }
+ }
+ for (i = 1; i < block_length + 1; i++)
+ o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+}
+
/* Find blocks of moved code, delegate actual coloring decision to helper */
static void mark_color_as_moved(struct diff_options *o,
struct hashmap *add_lines,
}
if (!match) {
- if (block_length < COLOR_MOVED_MIN_BLOCK_LENGTH &&
- o->color_moved != COLOR_MOVED_PLAIN) {
- for (i = 0; i < block_length + 1; i++) {
- l = &o->emitted_symbols->buf[n - i];
- l->flags &= ~DIFF_SYMBOL_MOVED_LINE;
- }
- }
+ adjust_last_block(o, n, block_length);
pmb_nr = 0;
block_length = 0;
continue;
}
l->flags |= DIFF_SYMBOL_MOVED_LINE;
- block_length++;
if (o->color_moved == COLOR_MOVED_PLAIN)
continue;
}
flipped_block = (flipped_block + 1) % 2;
+
+ adjust_last_block(o, n, block_length);
+ block_length = 0;
}
+ block_length++;
+
if (flipped_block)
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
}
+ adjust_last_block(o, n, block_length);
free(pmb);
}
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+ (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
+static void dim_moved_lines(struct diff_options *o)
+{
+ int n;
+ for (n = 0; n < o->emitted_symbols->nr; n++) {
+ struct emitted_diff_symbol *prev = (n != 0) ?
+ &o->emitted_symbols->buf[n - 1] : NULL;
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ struct emitted_diff_symbol *next =
+ (n < o->emitted_symbols->nr - 1) ?
+ &o->emitted_symbols->buf[n + 1] : NULL;
+
+ /* Not a plus or minus line? */
+ if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS)
+ continue;
+
+ /* Not a moved line? */
+ if (!(l->flags & DIFF_SYMBOL_MOVED_LINE))
+ continue;
+
+ /*
+ * If prev or next are not a plus or minus line,
+ * pretend they don't exist
+ */
+ if (prev && prev->s != DIFF_SYMBOL_PLUS &&
+ prev->s != DIFF_SYMBOL_MINUS)
+ prev = NULL;
+ if (next && next->s != DIFF_SYMBOL_PLUS &&
+ next->s != DIFF_SYMBOL_MINUS)
+ next = NULL;
+
+ /* Inside a block? */
+ if ((prev &&
+ (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) &&
+ (next &&
+ (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) {
+ l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+ continue;
+ }
+
+ /* Check if we are at an interesting bound: */
+ if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) &&
+ (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+ continue;
+ if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) &&
+ (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+ continue;
+
+ /*
+ * The boundary to prev and next are not interesting,
+ * so this line is not interesting as a whole
+ */
+ l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+ }
+}
+
static void emit_line_ws_markup(struct diff_options *o,
const char *set, const char *reset,
const char *line, int len, char sign,
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
break;
case DIFF_SYMBOL_PLUS:
- if (flags & DIFF_SYMBOL_MOVED_LINE_ALT)
+ switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT:
set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT);
- else if (flags & DIFF_SYMBOL_MOVED_LINE)
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE:
set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED);
- else
+ break;
+ default:
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, '+',
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
break;
case DIFF_SYMBOL_MINUS:
- if (flags & DIFF_SYMBOL_MOVED_LINE_ALT)
+ switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT:
set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT);
- else if (flags & DIFF_SYMBOL_MOVED_LINE)
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE:
set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED);
- else
+ break;
+ default:
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, '-',
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
{
int i;
for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
- if (is_tempfile_active(&diff_temp[i].tempfile))
+ if (is_tempfile_active(diff_temp[i].tempfile))
delete_tempfile(&diff_temp[i].tempfile);
diff_temp[i].name = NULL;
}
struct diff_words_buffer {
mmfile_t text;
- long alloc;
+ unsigned long alloc;
struct diff_words_orig {
const char *begin, *end;
} *orig;
unsigned long allot;
size_t l = len;
- if (ecb->truncate)
- return ecb->truncate(line, len);
cp = line;
allot = l;
while (0 < l) {
}
print_stat_summary_inserts_deletes(options, total_files, adds, dels);
+ strbuf_release(&out);
}
static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
* bytes per "line".
* This is stupid and ugly, but very cheap...
*/
- damage = (damage + 63) / 64;
+ damage = DIV_ROUND_UP(damage, 64);
ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
dir.files[dir.nr].name = file->name;
dir.files[dir.nr].changed = damage;
int fd;
if (lstat(s->path, &st) < 0) {
- if (errno == ENOENT) {
- err_empty:
- err = -1;
- empty:
- s->data = (char *)"";
- s->size = 0;
- return err;
- }
+ err_empty:
+ err = -1;
+ empty:
+ s->data = (char *)"";
+ s->size = 0;
+ return err;
}
s->size = xsize_t(st.st_size);
if (!s->size)
const struct object_id *oid,
int mode)
{
- int fd;
struct strbuf buf = STRBUF_INIT;
struct strbuf template = STRBUF_INIT;
char *path_dup = xstrdup(path);
strbuf_addstr(&template, "XXXXXX_");
strbuf_addstr(&template, base);
- fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
- if (fd < 0)
+ temp->tempfile = mks_tempfile_ts(template.buf, strlen(base) + 1);
+ if (!temp->tempfile)
die_errno("unable to create temp-file");
if (convert_to_working_tree(path,
(const char *)blob, (size_t)size, &buf)) {
blob = buf.buf;
size = buf.len;
}
- if (write_in_full(fd, blob, size) != size)
+ if (write_in_full(temp->tempfile->fd, blob, size) < 0 ||
+ close_tempfile_gently(temp->tempfile))
die_errno("unable to write temp-file");
- close_tempfile(&temp->tempfile);
- temp->name = get_tempfile_path(&temp->tempfile);
+ temp->name = get_tempfile_path(temp->tempfile);
oid_to_hex_r(temp->hex, oid);
xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
strbuf_release(&buf);
}
if (lstat(one->path, &st) < 0)
die_errno("stat '%s'", one->path);
- if (index_path(one->oid.hash, one->path, &st, 0))
+ if (index_path(&one->oid, one->path, &st, 0))
die("cannot hash %s", one->path);
}
}
strbuf_addch(&sb, ' ');
quote_c_style(p->two->path, &sb, NULL, 0);
}
+ strbuf_addch(&sb, '\n');
emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
sb.buf, sb.len, 0);
strbuf_release(&sb);
emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
sb.buf, sb.len, 0);
show_mode_change(opt, p, 0);
+ strbuf_release(&sb);
}
static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
strbuf_addf(&sb, " (%d%%)\n", similarity_index(p));
emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
sb.buf, sb.len, 0);
+ strbuf_release(&sb);
}
show_mode_change(opt, p, !p->score);
break;
add_lines_to_move_detection(o, &add_lines, &del_lines);
mark_color_as_moved(o, &add_lines, &del_lines);
+ if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
+ dim_moved_lines(o);
hashmap_free(&add_lines, 0);
hashmap_free(&del_lines, 0);