#include "diffcore.h"
#include "delta.h"
#include "xdiff-interface.h"
+#include "color.h"
static int use_size_cache;
+static int diff_detect_rename_default;
static int diff_rename_limit_default = -1;
-static int diff_use_color_default = 0;
-
-enum color_diff {
- DIFF_RESET = 0,
- DIFF_PLAIN = 1,
- DIFF_METAINFO = 2,
- DIFF_FRAGINFO = 3,
- DIFF_FILE_OLD = 4,
- DIFF_FILE_NEW = 5,
-};
-
-#define COLOR_NORMAL ""
-#define COLOR_BOLD "\033[1m"
-#define COLOR_DIM "\033[2m"
-#define COLOR_UL "\033[4m"
-#define COLOR_BLINK "\033[5m"
-#define COLOR_REVERSE "\033[7m"
-#define COLOR_RESET "\033[m"
-
-#define COLOR_BLACK "\033[30m"
-#define COLOR_RED "\033[31m"
-#define COLOR_GREEN "\033[32m"
-#define COLOR_YELLOW "\033[33m"
-#define COLOR_BLUE "\033[34m"
-#define COLOR_MAGENTA "\033[35m"
-#define COLOR_CYAN "\033[36m"
-#define COLOR_WHITE "\033[37m"
-
-static const char *diff_colors[] = {
- [DIFF_RESET] = COLOR_RESET,
- [DIFF_PLAIN] = COLOR_NORMAL,
- [DIFF_METAINFO] = COLOR_BOLD,
- [DIFF_FRAGINFO] = COLOR_CYAN,
- [DIFF_FILE_OLD] = COLOR_RED,
- [DIFF_FILE_NEW] = COLOR_GREEN,
+static int diff_use_color_default;
+
+static char diff_colors[][COLOR_MAXLEN] = {
+ "\033[m", /* reset */
+ "", /* normal */
+ "\033[1m", /* bold */
+ "\033[36m", /* cyan */
+ "\033[31m", /* red */
+ "\033[32m", /* green */
+ "\033[33m" /* yellow */
};
static int parse_diff_color_slot(const char *var, int ofs)
return DIFF_FILE_OLD;
if (!strcasecmp(var+ofs, "new"))
return DIFF_FILE_NEW;
+ if (!strcasecmp(var+ofs, "commit"))
+ return DIFF_COMMIT;
die("bad config variable '%s'", var);
}
-static const char *parse_diff_color_value(const char *value, const char *var)
-{
- if (!strcasecmp(value, "normal"))
- return COLOR_NORMAL;
- if (!strcasecmp(value, "bold"))
- return COLOR_BOLD;
- if (!strcasecmp(value, "dim"))
- return COLOR_DIM;
- if (!strcasecmp(value, "ul"))
- return COLOR_UL;
- if (!strcasecmp(value, "blink"))
- return COLOR_BLINK;
- if (!strcasecmp(value, "reverse"))
- return COLOR_REVERSE;
- if (!strcasecmp(value, "reset"))
- return COLOR_RESET;
- if (!strcasecmp(value, "black"))
- return COLOR_BLACK;
- if (!strcasecmp(value, "red"))
- return COLOR_RED;
- if (!strcasecmp(value, "green"))
- return COLOR_GREEN;
- if (!strcasecmp(value, "yellow"))
- return COLOR_YELLOW;
- if (!strcasecmp(value, "blue"))
- return COLOR_BLUE;
- if (!strcasecmp(value, "magenta"))
- return COLOR_MAGENTA;
- if (!strcasecmp(value, "cyan"))
- return COLOR_CYAN;
- if (!strcasecmp(value, "white"))
- return COLOR_WHITE;
- die("bad config value '%s' for variable '%s'", value, var);
-}
-
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
{
if (!strcmp(var, "diff.renamelimit")) {
diff_rename_limit_default = git_config_int(var, value);
return 0;
}
if (!strcmp(var, "diff.color")) {
+ diff_use_color_default = git_config_colorbool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "diff.renames")) {
if (!value)
- diff_use_color_default = 1; /* bool */
- else if (!strcasecmp(value, "auto"))
- diff_use_color_default = isatty(1);
- else if (!strcasecmp(value, "never"))
- diff_use_color_default = 0;
- else if (!strcasecmp(value, "always"))
- diff_use_color_default = 1;
- else
- diff_use_color_default = git_config_bool(var, value);
+ diff_detect_rename_default = DIFF_DETECT_RENAME;
+ else if (!strcasecmp(value, "copies") ||
+ !strcasecmp(value, "copy"))
+ diff_detect_rename_default = DIFF_DETECT_COPY;
+ else if (git_config_bool(var,value))
+ diff_detect_rename_default = DIFF_DETECT_RENAME;
return 0;
}
if (!strncmp(var, "diff.color.", 11)) {
int slot = parse_diff_color_slot(var, 11);
- diff_colors[slot] = parse_diff_color_value(value, var);
+ color_parse(value, var, diff_colors[slot]);
return 0;
}
return git_default_config(var, value);
return NULL;
needlen = quote_c_style(str, NULL, NULL, 0);
if (!needlen)
- return strdup(str);
+ return xstrdup(str);
xp = xmalloc(needlen + 1);
quote_c_style(str, xp, NULL, 0);
return xp;
return 0;
}
+struct diff_words_buffer {
+ mmfile_t text;
+ long alloc;
+ long current; /* output pointer */
+ int suppressed_newline;
+};
+
+static void diff_words_append(char *line, unsigned long len,
+ struct diff_words_buffer *buffer)
+{
+ if (buffer->text.size + len > buffer->alloc) {
+ buffer->alloc = (buffer->text.size + len) * 3 / 2;
+ buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
+ }
+ line++;
+ len--;
+ memcpy(buffer->text.ptr + buffer->text.size, line, len);
+ buffer->text.size += len;
+}
+
+struct diff_words_data {
+ struct xdiff_emit_state xm;
+ struct diff_words_buffer minus, plus;
+};
+
+static void print_word(struct diff_words_buffer *buffer, int len, int color,
+ int suppress_newline)
+{
+ const char *ptr;
+ int eol = 0;
+
+ if (len == 0)
+ return;
+
+ ptr = buffer->text.ptr + buffer->current;
+ buffer->current += len;
+
+ if (ptr[len - 1] == '\n') {
+ eol = 1;
+ len--;
+ }
+
+ fputs(diff_get_color(1, color), stdout);
+ fwrite(ptr, len, 1, stdout);
+ fputs(diff_get_color(1, DIFF_RESET), stdout);
+
+ if (eol) {
+ if (suppress_newline)
+ buffer->suppressed_newline = 1;
+ else
+ putchar('\n');
+ }
+}
+
+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
+{
+ struct diff_words_data *diff_words = priv;
+
+ if (diff_words->minus.suppressed_newline) {
+ if (line[0] != '+')
+ putchar('\n');
+ diff_words->minus.suppressed_newline = 0;
+ }
+
+ len--;
+ switch (line[0]) {
+ case '-':
+ print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+ break;
+ case '+':
+ print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+ break;
+ case ' ':
+ print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+ diff_words->minus.current += len;
+ break;
+ }
+}
+
+/* this executes the word diff on the accumulated buffers */
+static void diff_words_show(struct diff_words_data *diff_words)
+{
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+ mmfile_t minus, plus;
+ int i;
+
+ minus.size = diff_words->minus.text.size;
+ minus.ptr = xmalloc(minus.size);
+ memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
+ for (i = 0; i < minus.size; i++)
+ if (isspace(minus.ptr[i]))
+ minus.ptr[i] = '\n';
+ diff_words->minus.current = 0;
+
+ plus.size = diff_words->plus.text.size;
+ plus.ptr = xmalloc(plus.size);
+ memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
+ for (i = 0; i < plus.size; i++)
+ if (isspace(plus.ptr[i]))
+ plus.ptr[i] = '\n';
+ diff_words->plus.current = 0;
+
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = diff_words;
+ diff_words->xm.consume = fn_out_diff_words_aux;
+ xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+ free(minus.ptr);
+ free(plus.ptr);
+ diff_words->minus.text.size = diff_words->plus.text.size = 0;
+
+ if (diff_words->minus.suppressed_newline) {
+ putchar('\n');
+ diff_words->minus.suppressed_newline = 0;
+ }
+}
+
struct emit_callback {
struct xdiff_emit_state xm;
int nparents, color_diff;
const char **label_path;
+ struct diff_words_data *diff_words;
};
-static inline const char *get_color(int diff_use_color, enum color_diff ix)
+static void free_diff_words_data(struct emit_callback *ecbdata)
+{
+ if (ecbdata->diff_words) {
+ /* flush buffers */
+ if (ecbdata->diff_words->minus.text.size ||
+ ecbdata->diff_words->plus.text.size)
+ diff_words_show(ecbdata->diff_words);
+
+ if (ecbdata->diff_words->minus.text.ptr)
+ free (ecbdata->diff_words->minus.text.ptr);
+ if (ecbdata->diff_words->plus.text.ptr)
+ free (ecbdata->diff_words->plus.text.ptr);
+ free(ecbdata->diff_words);
+ ecbdata->diff_words = NULL;
+ }
+}
+
+const char *diff_get_color(int diff_use_color, enum color_diff ix)
{
if (diff_use_color)
return diff_colors[ix];
{
int i;
struct emit_callback *ecbdata = priv;
- const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
- const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
+ const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
+ const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
if (ecbdata->label_path[0]) {
printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
;
if (2 <= i && i < len && line[i] == ' ') {
ecbdata->nparents = i - 1;
- set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+ set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
}
else if (len < ecbdata->nparents)
set = reset;
else {
int nparents = ecbdata->nparents;
int color = DIFF_PLAIN;
- for (i = 0; i < nparents && len; i++) {
- if (line[i] == '-')
- color = DIFF_FILE_OLD;
- else if (line[i] == '+')
- color = DIFF_FILE_NEW;
- }
- set = get_color(ecbdata->color_diff, color);
+ if (ecbdata->diff_words && nparents != 1)
+ /* fall back to normal diff */
+ free_diff_words_data(ecbdata);
+ if (ecbdata->diff_words) {
+ if (line[0] == '-') {
+ diff_words_append(line, len,
+ &ecbdata->diff_words->minus);
+ return;
+ } else if (line[0] == '+') {
+ diff_words_append(line, len,
+ &ecbdata->diff_words->plus);
+ return;
+ }
+ if (ecbdata->diff_words->minus.text.size ||
+ ecbdata->diff_words->plus.text.size)
+ diff_words_show(ecbdata->diff_words);
+ line++;
+ len--;
+ } else
+ for (i = 0; i < nparents && len; i++) {
+ if (line[i] == '-')
+ color = DIFF_FILE_OLD;
+ else if (line[i] == '+')
+ color = DIFF_FILE_NEW;
+ }
+ set = diff_get_color(ecbdata->color_diff, color);
}
if (len > 0 && line[len-1] == '\n')
len--;
x->is_renamed = 1;
}
else
- x->name = strdup(name_a);
+ x->name = xstrdup(name_a);
return x;
}
return deflated;
}
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
{
void *cp;
void *delta;
unsigned long deflate_size;
unsigned long data_size;
- printf("GIT binary patch\n");
/* We could do deflated delta, or we could do just deflated two,
* whichever is smaller.
*/
free(data);
}
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+ printf("GIT binary patch\n");
+ emit_binary_diff_body(one, two);
+ emit_binary_diff_body(two, one);
+}
+
#define FIRST_FEW_BYTES 8000
static int mmfile_is_binary(mmfile_t *mf)
{
long sz = mf->size;
if (FIRST_FEW_BYTES < sz)
sz = FIRST_FEW_BYTES;
- if (memchr(mf->ptr, 0, sz))
- return 1;
- return 0;
+ return !!memchr(mf->ptr, 0, sz);
}
static void builtin_diff(const char *name_a,
mmfile_t mf1, mf2;
const char *lbl[2];
char *a_one, *b_two;
- const char *set = get_color(o->color_diff, DIFF_METAINFO);
- const char *reset = get_color(o->color_diff, DIFF_RESET);
+ const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
+ const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
a_one = quote_two("a/", name_a);
b_two = quote_two("b/", name_b);
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+ if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
ecbdata.xm.consume = fn_out_consume;
+ if (o->color_diff_words)
+ ecbdata.diff_words =
+ xcalloc(1, sizeof(struct diff_words_data));
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ if (o->color_diff_words)
+ free_diff_words_data(&ecbdata);
}
free_ab_and_return:
{
if (mode) {
spec->mode = canon_mode(mode);
- memcpy(spec->sha1, sha1, 20);
- spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
+ hashcpy(spec->sha1, sha1);
+ spec->sha1_valid = !is_null_sha1(sha1);
}
}
if ((lstat(name, &st) < 0) ||
!S_ISREG(st.st_mode) || /* careful! */
ce_match_stat(ce, &st, 0) ||
- memcmp(sha1, ce->sha1, 20))
+ hashcmp(sha1, ce->sha1))
return 0;
/* we return 1 only when we can stat, it is a regular file,
* stat information matches, and sha1 recorded in the cache
while (last > first) {
int cmp, next = (last + first) >> 1;
e = sha1_size_cache[next];
- cmp = memcmp(e->sha1, sha1, 20);
+ cmp = hashcmp(e->sha1, sha1);
if (!cmp)
return e;
if (cmp < 0) {
sizeof(*sha1_size_cache));
e = xmalloc(sizeof(struct sha1_size_cache));
sha1_size_cache[first] = e;
- memcpy(e->sha1, sha1, 20);
+ hashcpy(e->sha1, sha1);
e->size = size;
return e;
}
}
}
else
- memset(one->sha1, 0, 20);
+ hashclr(one->sha1);
}
static void run_diff(struct diff_filepair *p, struct diff_options *o)
;
}
- if (memcmp(one->sha1, two->sha1, 20)) {
+ if (hashcmp(one->sha1, two->sha1)) {
int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
+ if (o->binary) {
+ mmfile_t mf;
+ if ((!fill_mmfile(&mf, one) && mmfile_is_binary(&mf)) ||
+ (!fill_mmfile(&mf, two) && mmfile_is_binary(&mf)))
+ abbrev = 40;
+ }
len += snprintf(msg + len, sizeof(msg) - len,
"index %.*s..%.*s",
abbrev, sha1_to_hex(one->sha1),
options->change = diff_change;
options->add_remove = diff_addremove;
options->color_diff = diff_use_color_default;
+ options->detect_rename = diff_detect_rename_default;
}
int diff_setup_done(struct diff_options *options)
{
- if ((options->find_copies_harder &&
- options->detect_rename != DIFF_DETECT_COPY) ||
- (0 <= options->rename_limit && !options->detect_rename))
- return -1;
+ int count = 0;
+
+ if (options->output_format & DIFF_FORMAT_NAME)
+ count++;
+ if (options->output_format & DIFF_FORMAT_NAME_STATUS)
+ count++;
+ if (options->output_format & DIFF_FORMAT_CHECKDIFF)
+ count++;
+ if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
+ count++;
+ if (count > 1)
+ die("--name-only, --name-status, --check and -s are mutually exclusive");
+
+ if (options->find_copies_harder)
+ options->detect_rename = DIFF_DETECT_COPY;
if (options->output_format & (DIFF_FORMAT_NAME |
DIFF_FORMAT_NAME_STATUS |
options->full_index = 1;
else if (!strcmp(arg, "--binary")) {
options->output_format |= DIFF_FORMAT_PATCH;
- options->full_index = options->binary = 1;
+ options->binary = 1;
+ }
+ else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
+ options->text = 1;
}
else if (!strcmp(arg, "--name-only"))
options->output_format |= DIFF_FORMAT_NAME;
}
else if (!strcmp(arg, "--color"))
options->color_diff = 1;
+ else if (!strcmp(arg, "--no-color"))
+ options->color_diff = 0;
else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+ else if (!strcmp(arg, "--color-words"))
+ options->color_diff = options->color_diff_words = 1;
+ else if (!strcmp(arg, "--no-renames"))
+ options->detect_rename = 0;
else
return 0;
return 1;
struct diff_filespec *one,
struct diff_filespec *two)
{
- struct diff_filepair *dp = xmalloc(sizeof(*dp));
+ struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
dp->one = one;
dp->two = two;
- dp->score = 0;
- dp->status = 0;
- dp->source_stays = 0;
- dp->broken_pair = 0;
if (queue)
diff_q(queue, dp);
return dp;
* dealing with a change.
*/
if (one->sha1_valid && two->sha1_valid &&
- !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
+ !hashcmp(one->sha1, two->sha1))
return 1; /* no change */
if (!one->sha1_valid && !two->sha1_valid)
return 1; /* both look at the same file on the filesystem. */
if (!p->status)
p->status = DIFF_STATUS_RENAMED;
}
- else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
+ else if (hashcmp(p->one->sha1, p->two->sha1) ||
p->one->mode != p->two->mode)
p->status = DIFF_STATUS_MODIFIED;
else {
}
}
+ if (output_format & DIFF_FORMAT_CALLBACK)
+ options->format_callback(q, options, options->format_callback_data);
+
for (i = 0; i < q->nr; i++)
diff_free_filepair(q->queue[i]);
free_queue: