* name-a => name-b
*/
if (pfx_length + sfx_length) {
- name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+ int a_midlen = len_a - pfx_length - sfx_length;
+ int b_midlen = len_b - pfx_length - sfx_length;
+ if (a_midlen < 0) a_midlen = 0;
+ if (b_midlen < 0) b_midlen = 0;
+
+ name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
sprintf(name, "%.*s{%.*s => %.*s}%s",
pfx_length, a,
- len_a - pfx_length - sfx_length, a + pfx_length,
- len_b - pfx_length - sfx_length, b + pfx_length,
+ a_midlen, a + pfx_length,
+ b_midlen, b + pfx_length,
a + len_a - sfx_length);
}
else {
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)
ecbdata.label_path = lbl;
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 3;
+ xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (!diffopts)
;
}
}
+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));
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
+ options->context = 3;
options->change = diff_change;
options->add_remove = diff_addremove;
* 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) {
return 0;
}
+int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
+{
+ char c, *eq;
+ int len;
+
+ if (*arg != '-')
+ return 0;
+ c = *++arg;
+ if (!c)
+ return 0;
+ if (c == arg_short) {
+ c = *++arg;
+ if (!c)
+ return 1;
+ if (val && isdigit(c)) {
+ char *end;
+ int n = strtoul(arg, &end, 10);
+ if (*end)
+ return 0;
+ *val = n;
+ return 1;
+ }
+ return 0;
+ }
+ if (c != '-')
+ return 0;
+ arg++;
+ eq = strchr(arg, '=');
+ if (eq)
+ len = eq - arg;
+ else
+ len = strlen(arg);
+ if (!len || strncmp(arg, arg_long, len))
+ return 0;
+ if (eq) {
+ int n;
+ char *end;
+ if (!isdigit(*++eq))
+ return 0;
+ n = strtoul(eq, &end, 10);
+ if (*end)
+ return 0;
+ *val = n;
+ }
+ return 1;
+}
+
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{
const char *arg = av[0];
if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
options->output_format = DIFF_FORMAT_PATCH;
+ else if (opt_arg(arg, 'U', "unified", &options->context))
+ options->output_format = DIFF_FORMAT_PATCH;
else if (!strcmp(arg, "--patch-with-raw")) {
options->output_format = DIFF_FORMAT_PATCH;
options->with_raw = 1;
}
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")) {
options->output_format = DIFF_FORMAT_PATCH;
options->with_stat = 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;
}
}
+static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
+{
+ if (fs->mode)
+ printf(" %s mode %06o %s\n", newdelete, fs->mode, fs->path);
+ else
+ printf(" %s %s\n", newdelete, fs->path);
+}
+
+
+static void show_mode_change(struct diff_filepair *p, int show_name)
+{
+ if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
+ if (show_name)
+ printf(" mode change %06o => %06o %s\n",
+ p->one->mode, p->two->mode, p->two->path);
+ else
+ printf(" mode change %06o => %06o\n",
+ p->one->mode, p->two->mode);
+ }
+}
+
+static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
+{
+ const char *old, *new;
+
+ /* Find common prefix */
+ old = p->one->path;
+ new = p->two->path;
+ while (1) {
+ const char *slash_old, *slash_new;
+ slash_old = strchr(old, '/');
+ slash_new = strchr(new, '/');
+ if (!slash_old ||
+ !slash_new ||
+ slash_old - old != slash_new - new ||
+ memcmp(old, new, slash_new - new))
+ break;
+ old = slash_old + 1;
+ new = slash_new + 1;
+ }
+ /* p->one->path thru old is the common prefix, and old and new
+ * through the end of names are renames
+ */
+ if (old != p->one->path)
+ printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+ (int)(old - p->one->path), p->one->path,
+ old, new, (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ else
+ printf(" %s %s => %s (%d%%)\n", renamecopy,
+ p->one->path, p->two->path,
+ (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ show_mode_change(p, 0);
+}
+
+static void diff_summary(struct diff_filepair *p)
+{
+ switch(p->status) {
+ case DIFF_STATUS_DELETED:
+ show_file_mode_name("delete", p->one);
+ break;
+ case DIFF_STATUS_ADDED:
+ show_file_mode_name("create", p->two);
+ break;
+ case DIFF_STATUS_COPIED:
+ show_rename_copy("copy", p);
+ break;
+ case DIFF_STATUS_RENAMED:
+ show_rename_copy("rename", p);
+ break;
+ default:
+ if (p->score) {
+ printf(" rewrite %s (%d%%)\n", p->two->path,
+ (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ show_mode_change(p, 0);
+ } else show_mode_change(p, 1);
+ break;
+ }
+}
+
void diff_flush(struct diff_options *options)
{
struct diff_queue_struct *q = &diff_queued_diff;
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];
flush_one_pair(p, diff_output_format, options, diffstat);
- diff_free_filepair(p);
}
if (diffstat) {
free(diffstat);
}
+ for (i = 0; i < q->nr; i++) {
+ if (diffstat && options->summary)
+ diff_summary(q->queue[i]);
+ diff_free_filepair(q->queue[i]);
+ }
+
free(q->queue);
q->queue = NULL;
q->nr = q->alloc = 0;