return git_default_config(var, value);
}
+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"
+
+#define COLOR_CYANBG "\033[46m"
+#define COLOR_GRAYBG "\033[47m" // Good for xterm
+
+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 char *quote_one(const char *str)
{
int needlen;
}
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)
if (a_midlen < 0) a_midlen = 0;
if (b_midlen < 0) b_midlen = 0;
- name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+ name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
sprintf(name, "%.*s{%.*s => %.*s}%s",
pfx_length, a,
a_midlen, a + pfx_length,
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
+const char mime_boundary_leader[] = "------------";
static void show_stats(struct diffstat_t* data)
{
total_files, adds, dels);
}
+struct checkdiff_t {
+ struct xdiff_emit_state xm;
+ const char *filename;
+ int lineno;
+};
+
+static void checkdiff_consume(void *priv, char *line, unsigned long len)
+{
+ struct checkdiff_t *data = priv;
+
+ if (line[0] == '+') {
+ int i, spaces = 0;
+
+ data->lineno++;
+
+ /* check space before tab */
+ for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
+ if (line[i] == ' ')
+ spaces++;
+ if (line[i - 1] == '\t' && spaces)
+ printf("%s:%d: space before tab:%.*s\n",
+ data->filename, data->lineno, (int)len, line);
+
+ /* check white space at line end */
+ if (line[len - 1] == '\n')
+ len--;
+ if (isspace(line[len - 1]))
+ printf("%s:%d: white space at end: %.*s\n",
+ data->filename, data->lineno, (int)len, line);
+ } else if (line[0] == ' ')
+ data->lineno++;
+ else if (line[0] == '@') {
+ char *plus = strchr(line, '+');
+ if (plus)
+ data->lineno = strtol(plus, NULL, 10);
+ else
+ die("invalid diff");
+ }
+}
+
static unsigned char *deflate_it(char *data,
unsigned long size,
unsigned long *result_size)
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_PLAIN);
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;
+ ecbdata.color_diff = o->color_diff;
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
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);
}
}
}
+static void builtin_checkdiff(const char *name_a, const char *name_b,
+ struct diff_filespec *one,
+ struct diff_filespec *two)
+{
+ mmfile_t mf1, mf2;
+ struct checkdiff_t data;
+
+ if (!two)
+ return;
+
+ memset(&data, 0, sizeof(data));
+ data.xm.consume = checkdiff_consume;
+ data.filename = name_b ? name_b : name_a;
+ data.lineno = 0;
+
+ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ die("unable to read files to diff");
+
+ if (mmfile_is_binary(&mf2))
+ return;
+ else {
+ /* Crazy xdl interfaces.. */
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 0;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = &data;
+ xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ }
+}
+
struct diff_filespec *alloc_filespec(const char *path)
{
int namelen = strlen(path);
builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
}
+static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
+{
+ const char *name;
+ const char *other;
+
+ if (DIFF_PAIR_UNMERGED(p)) {
+ /* unmerged */
+ return;
+ }
+
+ name = p->one->path;
+ other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+
+ diff_fill_sha1_info(p->one);
+ diff_fill_sha1_info(p->two);
+
+ builtin_checkdiff(name, other, p->one, p->two);
+}
+
void diff_setup(struct diff_options *options)
{
memset(options, 0, sizeof(*options));
* recursive bits for other formats here.
*/
if ((options->output_format == DIFF_FORMAT_PATCH) ||
- (options->output_format == DIFF_FORMAT_DIFFSTAT))
+ (options->output_format == DIFF_FORMAT_DIFFSTAT) ||
+ (options->output_format == 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) {
}
else if (!strcmp(arg, "--stat"))
options->output_format = DIFF_FORMAT_DIFFSTAT;
+ else if (!strcmp(arg, "--check"))
+ options->output_format = DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
options->summary = 1;
else if (!strcmp(arg, "--patch-with-stat")) {
else if (40 < options->abbrev)
options->abbrev = 40;
}
+ else if (!strcmp(arg, "--color"))
+ options->color_diff = 1;
else
return 0;
return 1;
run_diffstat(p, o, diffstat);
}
+static void diff_flush_checkdiff(struct diff_filepair *p,
+ struct diff_options *o)
+{
+ if (diff_unmodified_pair(p))
+ return;
+
+ if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+ (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+ return; /* no tree diffs in patch format */
+
+ run_checkdiff(p, o);
+}
+
int diff_queue_is_empty(void)
{
struct diff_queue_struct *q = &diff_queued_diff;
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;
show_stats(diffstat);
free(diffstat);
diffstat = NULL;
- putchar(options->line_termination);
+ 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];
}
for (i = 0; i < q->nr; i++) {
- if (options->summary)
+ if (diffstat && options->summary)
diff_summary(q->queue[i]);
diff_free_filepair(q->queue[i]);
}