diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
+ } else if (!prefixcmp(line, "\\ ")) {
+ /*
+ * Eat the "no newline at eof" marker as if we
+ * saw a "+" or "-" line with nothing on it,
+ * and return without diff_words_flush() to
+ * defer processing. If this is the end of
+ * preimage, more "+" lines may come after it.
+ */
+ return;
}
diff_words_flush(ecbdata);
if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
static int scale_linear(int it, int width, int max_change)
{
+ if (!it)
+ return 0;
/*
- * make sure that at least one '-' is printed if there were deletions,
- * and likewise for '+'.
+ * make sure that at least one '-' or '+' is printed if
+ * there is any change to this path. The easiest way is to
+ * scale linearly as if the alloted width is one column shorter
+ * than it is, and then add 1 to the result.
*/
- if (max_change < 2)
- return it;
- return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
+ return 1 + (it * (width - 1) / max_change);
}
static void show_name(FILE *file,
file->print_name = pname;
}
+int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret;
+
+ if (!files) {
+ assert(insertions == 0 && deletions == 0);
+ return fputs(_(" 0 files changed\n"), fp);
+ }
+
+ strbuf_addf(&sb,
+ Q_(" %d file changed", " %d files changed", files),
+ files);
+
+ /*
+ * For binary diff, the caller may want to print "x files
+ * changed" with insertions == 0 && deletions == 0.
+ *
+ * Not omitting "0 insertions(+), 0 deletions(-)" in this case
+ * is probably less confusing (i.e skip over "2 files changed
+ * but nothing about added/removed lines? Is this a bug in Git?").
+ */
+ if (insertions || deletions == 0) {
+ /*
+ * TRANSLATORS: "+" in (+) is a line addition marker;
+ * do not translate it.
+ */
+ strbuf_addf(&sb,
+ Q_(", %d insertion(+)", ", %d insertions(+)",
+ insertions),
+ insertions);
+ }
+
+ if (deletions || insertions == 0) {
+ /*
+ * TRANSLATORS: "-" in (-) is a line removal marker;
+ * do not translate it.
+ */
+ strbuf_addf(&sb,
+ Q_(", %d deletion(-)", ", %d deletions(-)",
+ deletions),
+ deletions);
+ }
+ strbuf_addch(&sb, '\n');
+ ret = fputs(sb.buf, fp);
+ strbuf_release(&sb);
+ return ret;
+}
+
static void show_stats(struct diffstat_t *data, struct diff_options *options)
{
int i, len, add, del, adds = 0, dels = 0;
uintmax_t max_change = 0, max_len = 0;
int total_files = data->nr;
- int width, name_width;
+ int width, name_width, count;
const char *reset, *add_c, *del_c;
const char *line_prefix = "";
+ int extra_shown = 0;
struct strbuf *msg = NULL;
if (data->nr == 0)
width = options->stat_width ? options->stat_width : 80;
name_width = options->stat_name_width ? options->stat_name_width : 50;
+ count = options->stat_count ? options->stat_count : data->nr;
/* Sanity: give at least 5 columns to the graph,
* but leave at least 10 columns for the name.
add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
- for (i = 0; i < data->nr; i++) {
+ for (i = 0; (i < count) && (i < data->nr); i++) {
struct diffstat_file *file = data->files[i];
uintmax_t change = file->added + file->deleted;
+ if (!data->files[i]->is_renamed &&
+ (change == 0)) {
+ count++; /* not shown == room for one more */
+ continue;
+ }
fill_print_name(file);
len = strlen(file->print_name);
if (max_len < len)
if (max_change < change)
max_change = change;
}
+ count = i; /* min(count, data->nr) */
/* Compute the width of the graph part;
* 10 is for one blank at the beginning of the line plus
else
width = max_change;
- for (i = 0; i < data->nr; i++) {
+ for (i = 0; i < count; i++) {
const char *prefix = "";
char *name = data->files[i]->print_name;
uintmax_t added = data->files[i]->added;
uintmax_t deleted = data->files[i]->deleted;
int name_len;
+ if (!data->files[i]->is_renamed &&
+ (added + deleted == 0)) {
+ total_files--;
+ continue;
+ }
/*
* "scale" the filename
*/
fprintf(options->file, " Unmerged\n");
continue;
}
- else if (!data->files[i]->is_renamed &&
- (added + deleted == 0)) {
- total_files--;
- continue;
- }
/*
* scale the add/delete
dels += del;
if (width <= max_change) {
- add = scale_linear(add, width, max_change);
- del = scale_linear(del, width, max_change);
+ int total = add + del;
+
+ total = scale_linear(add + del, width, max_change);
+ if (total < 2 && add && del)
+ /* width >= 2 due to the sanity check */
+ total = 2;
+ if (add < del) {
+ add = scale_linear(add, width, max_change);
+ del = total - add;
+ } else {
+ del = scale_linear(del, width, max_change);
+ add = total - del;
+ }
}
fprintf(options->file, "%s", line_prefix);
show_name(options->file, prefix, name, len);
show_graph(options->file, '-', del, del_c, reset);
fprintf(options->file, "\n");
}
+ for (i = count; i < data->nr; i++) {
+ uintmax_t added = data->files[i]->added;
+ uintmax_t deleted = data->files[i]->deleted;
+ if (!data->files[i]->is_renamed &&
+ (added + deleted == 0)) {
+ total_files--;
+ continue;
+ }
+ adds += added;
+ dels += deleted;
+ if (!extra_shown)
+ fprintf(options->file, "%s ...\n", line_prefix);
+ extra_shown = 1;
+ }
fprintf(options->file, "%s", line_prefix);
- fprintf(options->file,
- " %d files changed, %d insertions(+), %d deletions(-)\n",
- total_files, adds, dels);
+ print_stat_summary(options->file, total_files, adds, dels);
}
static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
options->output_prefix_data);
fprintf(options->file, "%s", msg->buf);
}
- fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
- total_files, adds, dels);
+ print_stat_summary(options->file, total_files, adds, dels);
}
static void show_numstat(struct diffstat_t *data, struct diff_options *options)
struct emit_callback ecbdata;
const struct userdiff_funcname *pe;
- if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS) || must_show_header) {
+ if (must_show_header) {
fprintf(o->file, "%s", header.buf);
strbuf_reset(&header);
}
xecfg.ctxlen = o->context;
xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
+ if (DIFF_OPT_TST(o, FUNCCONTEXT))
+ xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
if (pe)
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
if (!diffopts)
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = o->xdl_opts;
+ xecfg.ctxlen = o->context;
+ xecfg.interhunkctxlen = o->interhunkcontext;
xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
&xpp, &xecfg);
}
char *end;
int width = options->stat_width;
int name_width = options->stat_name_width;
+ int count = options->stat_count;
int argcount = 1;
arg += strlen("--stat");
name_width = strtoul(av[1], &end, 10);
argcount = 2;
}
+ } else if (!prefixcmp(arg, "-count")) {
+ arg += strlen("-count");
+ if (*arg == '=')
+ count = strtoul(arg + 1, &end, 10);
+ else if (!*arg && !av[1])
+ die("Option '--stat-count' requires a value");
+ else if (!*arg) {
+ count = strtoul(av[1], &end, 10);
+ argcount = 2;
+ }
}
break;
case '=':
width = strtoul(arg+1, &end, 10);
if (*end == ',')
name_width = strtoul(end+1, &end, 10);
+ if (*end == ',')
+ count = strtoul(end+1, &end, 10);
}
/* Important! This checks all the error cases! */
options->output_format |= DIFF_FORMAT_DIFFSTAT;
options->stat_name_width = name_width;
options->stat_width = width;
+ options->stat_count = count;
return argcount;
}
else if (!strcmp(arg, "-s"))
options->output_format |= DIFF_FORMAT_NO_OUTPUT;
else if (!prefixcmp(arg, "--stat"))
- /* --stat, --stat-width, or --stat-name-width */
+ /* --stat, --stat-width, --stat-name-width, or --stat-count */
return stat_opt(options, av);
/* renames options */
}
/* xdiff options */
+ else if (!strcmp(arg, "--minimal"))
+ DIFF_XDL_SET(options, NEED_MINIMAL);
+ else if (!strcmp(arg, "--no-minimal"))
+ DIFF_XDL_CLR(options, NEED_MINIMAL);
else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
DIFF_XDL_SET(options, IGNORE_WHITESPACE);
else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
else if (!strcmp(arg, "--patience"))
DIFF_XDL_SET(options, PATIENCE_DIFF);
+ else if (!strcmp(arg, "--histogram"))
+ DIFF_XDL_SET(options, HISTOGRAM_DIFF);
/* flags options */
else if (!strcmp(arg, "--binary")) {
else if (opt_arg(arg, '\0', "inter-hunk-context",
&options->interhunkcontext))
;
+ else if (!strcmp(arg, "-W"))
+ DIFF_OPT_SET(options, FUNCCONTEXT);
+ else if (!strcmp(arg, "--function-context"))
+ DIFF_OPT_SET(options, FUNCCONTEXT);
+ else if (!strcmp(arg, "--no-function-context"))
+ DIFF_OPT_CLR(options, FUNCCONTEXT);
else if ((argcount = parse_long_opt("output", av, &optarg))) {
options->file = fopen(optarg, "w");
if (!options->file)