static int use_size_cache;
-int diff_rename_limit_default = -1;
+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 parse_diff_color_slot(const char *var, int ofs)
+{
+ if (!strcasecmp(var+ofs, "plain"))
+ return DIFF_PLAIN;
+ if (!strcasecmp(var+ofs, "meta"))
+ return DIFF_METAINFO;
+ if (!strcasecmp(var+ofs, "frag"))
+ return DIFF_FRAGINFO;
+ if (!strcasecmp(var+ofs, "old"))
+ return DIFF_FILE_OLD;
+ if (!strcasecmp(var+ofs, "new"))
+ return DIFF_FILE_NEW;
+ 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)
{
diff_rename_limit_default = git_config_int(var, value);
return 0;
}
-
+ if (!strcmp(var, "diff.color")) {
+ 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);
+ 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);
+ return 0;
+ }
return git_default_config(var, value);
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
- mf->ptr = ""; /* does not matter */
+ mf->ptr = (char *)""; /* does not matter */
mf->size = 0;
return 0;
}
}
struct emit_callback {
+ struct xdiff_emit_state xm;
+ int nparents, color_diff;
const char **label_path;
};
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+static inline const char *get_color(int diff_use_color, enum color_diff ix)
+{
+ if (diff_use_color)
+ return diff_colors[ix];
+ return "";
+}
+
+static void fn_out_consume(void *priv, char *line, unsigned long len)
{
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);
if (ecbdata->label_path[0]) {
- printf("--- %s\n", ecbdata->label_path[0]);
- printf("+++ %s\n", ecbdata->label_path[1]);
+ printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
+ printf("%s+++ %s%s\n", set, ecbdata->label_path[1], reset);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
- for (i = 0; i < nbuf; i++)
- if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
- return -1;
- return 0;
+
+ /* This is not really necessary for now because
+ * this codepath only deals with two-way diffs.
+ */
+ for (i = 0; i < len && line[i] == '@'; i++)
+ ;
+ if (2 <= i && i < len && line[i] == ' ') {
+ ecbdata->nparents = i - 1;
+ set = 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 (len > 0 && line[len-1] == '\n')
+ len--;
+ printf("%s%.*s%s\n", set, (int) len, line, reset);
}
static char *pprint_rename(const char *a, const char *b)
}
for (i = 0; i < data->nr; i++) {
- char *prefix = "";
+ const char *prefix = "";
char *name = data->files[i]->name;
int added = data->files[i]->added;
int deleted = data->files[i]->deleted;
else
line[0] = bytes - 26 + 'a' - 1;
encode_85(line + 1, cp, bytes);
- cp += bytes;
+ cp = (char *) cp + bytes;
puts(line);
}
printf("\n");
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);
a_one = quote_two("a/", name_a);
b_two = quote_two("b/", name_b);
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
- printf("diff --git %s %s\n", a_one, b_two);
+ printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
if (lbl[0][0] == '/') {
/* /dev/null */
- printf("new file mode %06o\n", two->mode);
+ printf("%snew file mode %06o%s\n", set, two->mode, reset);
if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
+ printf("%s%s%s\n", set, xfrm_msg, reset);
}
else if (lbl[1][0] == '/') {
- printf("deleted file mode %06o\n", one->mode);
+ printf("%sdeleted file mode %06o%s\n", set, one->mode, reset);
if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
+ printf("%s%s%s\n", set, xfrm_msg, reset);
}
else {
if (one->mode != two->mode) {
- printf("old mode %06o\n", one->mode);
- printf("new mode %06o\n", two->mode);
+ printf("%sold mode %06o%s\n", set, one->mode, reset);
+ printf("%snew mode %06o%s\n", set, two->mode, reset);
}
if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
+ printf("%s%s%s\n", set, xfrm_msg, reset);
/*
* we do not run diff between different kind
* of objects.
xdemitcb_t ecb;
struct emit_callback ecbdata;
+ memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
- xpp.flags = XDF_NEED_MINIMAL;
+ ecbdata.color_diff = o->color_diff;
+ xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (!diffopts)
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!strncmp(diffopts, "-u", 2))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- ecb.outf = fn_out;
+ ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
+ ecbdata.xm.consume = fn_out_consume;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
}
struct diff_filespec *one,
struct diff_filespec *two,
struct diffstat_t *diffstat,
+ struct diff_options *o,
int complete_rewrite)
{
mmfile_t mf1, mf2;
xdemitconf_t xecfg;
xdemitcb_t ecb;
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = 0;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
err_empty:
err = -1;
empty:
- s->data = "";
+ s->data = (char *)"";
s->size = 0;
return err;
}
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0);
+ builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, 0);
return;
}
if (p->status == DIFF_STATUS_MODIFIED && p->score)
complete_rewrite = 1;
- builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
+ builtin_diffstat(name, other, p->one, p->two, diffstat, o, complete_rewrite);
}
static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
void diff_setup(struct diff_options *options)
{
memset(options, 0, sizeof(*options));
- options->output_format = DIFF_FORMAT_RAW;
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
options->context = 3;
+ options->msg_sep = "\n";
options->change = diff_change;
options->add_remove = diff_addremove;
+ options->color_diff = diff_use_color_default;
}
int diff_setup_done(struct diff_options *options)
(0 <= options->rename_limit && !options->detect_rename))
return -1;
+ if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
+ options->output_format = 0;
+
+ if (options->output_format & (DIFF_FORMAT_NAME |
+ DIFF_FORMAT_NAME_STATUS |
+ DIFF_FORMAT_CHECKDIFF |
+ DIFF_FORMAT_NO_OUTPUT))
+ options->output_format &= ~(DIFF_FORMAT_RAW |
+ DIFF_FORMAT_DIFFSTAT |
+ DIFF_FORMAT_SUMMARY |
+ DIFF_FORMAT_PATCH);
+
/*
* These cases always need recursive; we do not drop caller-supplied
* recursive bits for other formats here.
*/
- if ((options->output_format == DIFF_FORMAT_PATCH) ||
- (options->output_format == DIFF_FORMAT_DIFFSTAT) ||
- (options->output_format == DIFF_FORMAT_CHECKDIFF))
+ if (options->output_format & (DIFF_FORMAT_PATCH |
+ DIFF_FORMAT_DIFFSTAT |
+ DIFF_FORMAT_CHECKDIFF))
options->recursive = 1;
- /*
- * These combinations do not make sense.
- */
- if (options->output_format == DIFF_FORMAT_RAW)
- options->with_raw = 0;
- if (options->output_format == DIFF_FORMAT_DIFFSTAT)
- options->with_stat = 0;
-
if (options->detect_rename && options->rename_limit < 0)
options->rename_limit = diff_rename_limit_default;
if (options->setup & DIFF_SETUP_USE_CACHE) {
return 0;
}
-int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
+static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
{
char c, *eq;
int len;
{
const char *arg = av[0];
if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
- options->output_format = DIFF_FORMAT_PATCH;
+ options->output_format |= DIFF_FORMAT_PATCH;
else if (opt_arg(arg, 'U', "unified", &options->context))
- options->output_format = DIFF_FORMAT_PATCH;
+ options->output_format |= DIFF_FORMAT_PATCH;
+ else if (!strcmp(arg, "--raw"))
+ options->output_format |= DIFF_FORMAT_RAW;
else if (!strcmp(arg, "--patch-with-raw")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_raw = 1;
+ options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
}
else if (!strcmp(arg, "--stat"))
- options->output_format = DIFF_FORMAT_DIFFSTAT;
+ options->output_format |= DIFF_FORMAT_DIFFSTAT;
else if (!strcmp(arg, "--check"))
- options->output_format = DIFF_FORMAT_CHECKDIFF;
+ options->output_format |= DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
- options->summary = 1;
+ options->output_format |= DIFF_FORMAT_SUMMARY;
else if (!strcmp(arg, "--patch-with-stat")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_stat = 1;
+ options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
}
else if (!strcmp(arg, "-z"))
options->line_termination = 0;
else if (!strcmp(arg, "--full-index"))
options->full_index = 1;
else if (!strcmp(arg, "--binary")) {
- options->output_format = DIFF_FORMAT_PATCH;
+ options->output_format |= DIFF_FORMAT_PATCH;
options->full_index = options->binary = 1;
}
else if (!strcmp(arg, "--name-only"))
- options->output_format = DIFF_FORMAT_NAME;
+ options->output_format |= DIFF_FORMAT_NAME;
else if (!strcmp(arg, "--name-status"))
- options->output_format = DIFF_FORMAT_NAME_STATUS;
+ options->output_format |= DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(arg, "-R"))
options->reverse_diff = 1;
else if (!strncmp(arg, "-S", 2))
options->pickaxe = arg + 2;
- else if (!strcmp(arg, "-s"))
- options->output_format = DIFF_FORMAT_NO_OUTPUT;
+ else if (!strcmp(arg, "-s")) {
+ options->output_format |= DIFF_FORMAT_NO_OUTPUT;
+ }
else if (!strncmp(arg, "-O", 2))
options->orderfile = arg + 2;
else if (!strncmp(arg, "--diff-filter=", 14))
else if (40 < options->abbrev)
options->abbrev = 40;
}
+ else if (!strcmp(arg, "--color"))
+ options->color_diff = 1;
+ 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
return 0;
return 1;
}
static void diff_flush_raw(struct diff_filepair *p,
- int line_termination,
- int inter_name_termination,
- struct diff_options *options,
- int output_format)
+ struct diff_options *options)
{
int two_paths;
char status[10];
int abbrev = options->abbrev;
const char *path_one, *path_two;
+ int inter_name_termination = '\t';
+ int line_termination = options->line_termination;
+
+ if (!line_termination)
+ inter_name_termination = 0;
path_one = p->one->path;
path_two = p->two->path;
two_paths = 0;
break;
}
- if (output_format != DIFF_FORMAT_NAME_STATUS) {
+ if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) {
printf(":%06o %06o %s ",
p->one->mode, p->two->mode,
diff_unique_abbrev(p->one->sha1, abbrev));
free((void*)path_two);
}
-static void diff_flush_name(struct diff_filepair *p,
- int inter_name_termination,
- int line_termination)
+static void diff_flush_name(struct diff_filepair *p, int line_termination)
{
char *path = p->two->path;
if (line_termination)
path = quote_one(p->two->path);
- else
- path = p->two->path;
printf("%s%c", path, line_termination);
if (p->two->path != path)
free(path);
diff_debug_queue("resolve-rename-copy done", q);
}
-static void flush_one_pair(struct diff_filepair *p,
- int diff_output_format,
- struct diff_options *options,
- struct diffstat_t *diffstat)
+static int check_pair_status(struct diff_filepair *p)
{
- int inter_name_termination = '\t';
- int line_termination = options->line_termination;
- if (!line_termination)
- inter_name_termination = 0;
-
switch (p->status) {
case DIFF_STATUS_UNKNOWN:
- break;
+ return 0;
case 0:
die("internal error in diff-resolve-rename-copy");
- break;
default:
- switch (diff_output_format) {
- case DIFF_FORMAT_DIFFSTAT:
- diff_flush_stat(p, options, diffstat);
- break;
- case DIFF_FORMAT_CHECKDIFF:
- diff_flush_checkdiff(p, options);
- break;
- case DIFF_FORMAT_PATCH:
- diff_flush_patch(p, options);
- break;
- case DIFF_FORMAT_RAW:
- case DIFF_FORMAT_NAME_STATUS:
- diff_flush_raw(p, line_termination,
- inter_name_termination,
- options, diff_output_format);
- break;
- case DIFF_FORMAT_NAME:
- diff_flush_name(p,
- inter_name_termination,
- line_termination);
- break;
- case DIFF_FORMAT_NO_OUTPUT:
- break;
- }
+ return 1;
}
}
+static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
+{
+ int fmt = opt->output_format;
+
+ if (fmt & DIFF_FORMAT_CHECKDIFF)
+ diff_flush_checkdiff(p, opt);
+ else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
+ diff_flush_raw(p, opt);
+ else if (fmt & DIFF_FORMAT_NAME)
+ diff_flush_name(p, opt->line_termination);
+}
+
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
{
if (fs->mode)
}
}
-void diff_flush(struct diff_options *options)
+static int is_summary_empty(const struct diff_queue_struct *q)
{
- struct diff_queue_struct *q = &diff_queued_diff;
int i;
- int diff_output_format = options->output_format;
- struct diffstat_t *diffstat = NULL;
- if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
- diffstat = xcalloc(sizeof (struct diffstat_t), 1);
- diffstat->xm.consume = diffstat_consume;
+ for (i = 0; i < q->nr; i++) {
+ const struct diff_filepair *p = q->queue[i];
+
+ switch (p->status) {
+ case DIFF_STATUS_DELETED:
+ case DIFF_STATUS_ADDED:
+ case DIFF_STATUS_COPIED:
+ case DIFF_STATUS_RENAMED:
+ return 0;
+ default:
+ if (p->score)
+ return 0;
+ if (p->one->mode && p->two->mode &&
+ p->one->mode != p->two->mode)
+ return 0;
+ break;
+ }
}
+ return 1;
+}
+
+void diff_flush(struct diff_options *options)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i, output_format = options->output_format;
+ int separator = 0;
- if (options->with_raw) {
+ /*
+ * Order: raw, stat, summary, patch
+ * or: name/name-status/checkdiff (other bits clear)
+ */
+ if (!q->nr)
+ goto free_queue;
+
+ if (output_format & (DIFF_FORMAT_RAW |
+ DIFF_FORMAT_NAME |
+ DIFF_FORMAT_NAME_STATUS |
+ DIFF_FORMAT_CHECKDIFF)) {
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
+ if (check_pair_status(p))
+ flush_one_pair(p, options);
}
- putchar(options->line_termination);
+ separator++;
}
- if (options->with_stat) {
+
+ if (output_format & DIFF_FORMAT_DIFFSTAT) {
+ struct diffstat_t diffstat;
+
+ if (separator++)
+ putchar('\n');
+
+ 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];
- flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
- diffstat);
+ if (check_pair_status(p))
+ diff_flush_stat(p, options, &diffstat);
}
- show_stats(diffstat);
- free(diffstat);
- diffstat = NULL;
- if (options->summary)
- for (i = 0; i < q->nr; i++)
- diff_summary(q->queue[i]);
- if (options->stat_sep)
- fputs(options->stat_sep, stdout);
- else
- putchar(options->line_termination);
- }
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- flush_one_pair(p, diff_output_format, options, diffstat);
+ show_stats(&diffstat);
}
- if (diffstat) {
- show_stats(diffstat);
- free(diffstat);
- }
+ if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
+ if (separator++)
+ putchar('\n');
- for (i = 0; i < q->nr; i++) {
- if (diffstat && options->summary)
+ for (i = 0; i < q->nr; i++)
diff_summary(q->queue[i]);
- diff_free_filepair(q->queue[i]);
}
+ if (output_format & DIFF_FORMAT_PATCH) {
+ if (separator) {
+ if (options->stat_sep) {
+ /* attach patch instead of inline */
+ fputs(options->stat_sep, stdout);
+ } else {
+ putchar(options->line_termination);
+ }
+ }
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ if (check_pair_status(p))
+ diff_flush_patch(p, options);
+ }
+ }
+
+ for (i = 0; i < q->nr; i++)
+ diff_free_filepair(q->queue[i]);
+free_queue:
free(q->queue);
q->queue = NULL;
q->nr = q->alloc = 0;