int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options;
static char diff_colors[][COLOR_MAXLEN] = {
return -1;
}
-static int parse_dirstat_params(struct diff_options *options, const char *params)
+static int parse_dirstat_params(struct diff_options *options, const char *params,
+ struct strbuf *errmsg)
{
const char *p = params;
+ int p_len, ret = 0;
+
while (*p) {
- if (!prefixcmp(p, "changes")) {
- p += 7;
+ p_len = strchrnul(p, ',') - p;
+ if (!memcmp(p, "changes", p_len)) {
+ DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
+ DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+ } else if (!memcmp(p, "lines", p_len)) {
+ DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
- } else if (!prefixcmp(p, "files")) {
- p += 5;
+ } else if (!memcmp(p, "files", p_len)) {
+ DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
- } else if (!prefixcmp(p, "noncumulative")) {
- p += 13;
+ } else if (!memcmp(p, "noncumulative", p_len)) {
DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
- } else if (!prefixcmp(p, "cumulative")) {
- p += 10;
+ } else if (!memcmp(p, "cumulative", p_len)) {
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
} else if (isdigit(*p)) {
char *end;
- options->dirstat_percent = strtoul(p, &end, 10);
- p = end;
- } else
- return error("Unknown --dirstat parameter '%s'", p);
-
- if (*p) {
- /* more parameters, swallow separator */
- if (*p != ',')
- return error("Missing comma separator at char "
- "%"PRIuMAX" of '%s'",
- (uintmax_t) (p - params), params);
- p++;
+ int permille = strtoul(p, &end, 10) * 10;
+ if (*end == '.' && isdigit(*++end)) {
+ /* only use first digit */
+ permille += *end - '0';
+ /* .. and ignore any further digits */
+ while (isdigit(*++end))
+ ; /* nothing */
+ }
+ if (end - p == p_len)
+ options->dirstat_permille = permille;
+ else {
+ strbuf_addf(errmsg, _(" Failed to parse dirstat cut-off percentage '%.*s'\n"),
+ p_len, p);
+ ret++;
+ }
+ } else {
+ strbuf_addf(errmsg, _(" Unknown dirstat parameter '%.*s'\n"),
+ p_len, p);
+ ret++;
}
+
+ p += p_len;
+
+ if (*p)
+ p++; /* more parameters, swallow separator */
}
- return 0;
+ return ret;
}
static int git_config_rename(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "diff.dirstat")) {
+ struct strbuf errmsg = STRBUF_INIT;
+ default_diff_options.dirstat_permille = diff_dirstat_permille_default;
+ if (parse_dirstat_params(&default_diff_options, value, &errmsg))
+ warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
+ errmsg.buf);
+ strbuf_release(&errmsg);
+ diff_dirstat_permille_default = default_diff_options.dirstat_permille;
+ return 0;
+ }
+
if (!prefixcmp(var, "submodule."))
return parse_submodule_config_option(var, value);
emit_line(ecbdata->opt, plain, reset, line, len);
fputs("~\n", ecbdata->opt->file);
} else {
- /* don't print the prefix character */
- emit_line(ecbdata->opt, plain, reset, line+1, len-1);
+ /*
+ * Skip the prefix character, if any. With
+ * diff_suppress_blank_empty, there may be
+ * none.
+ */
+ if (line[0] != '\n') {
+ line++;
+ len--;
+ }
+ emit_line(ecbdata->opt, plain, reset, line, len);
}
return;
}
struct dirstat_dir {
struct dirstat_file *files;
- int alloc, nr, percent, cumulative;
+ int alloc, nr, permille, cumulative;
};
static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
if (baselen && sources != 1) {
if (this_dir) {
int permille = this_dir * 1000 / changed;
- int percent = permille / 10;
- if (percent >= dir->percent) {
+ if (permille >= dir->permille) {
fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
- percent, permille % 10, baselen, base);
+ permille / 10, permille % 10, baselen, base);
if (!dir->cumulative)
return 0;
}
dir.files = NULL;
dir.alloc = 0;
dir.nr = 0;
- dir.percent = options->dirstat_percent;
+ dir.permille = options->dirstat_permille;
dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
changed = 0;
gather_dirstat(options, &dir, changed, "", 0);
}
+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
+{
+ int i;
+ unsigned long changed;
+ struct dirstat_dir dir;
+
+ if (data->nr == 0)
+ return;
+
+ dir.files = NULL;
+ dir.alloc = 0;
+ dir.nr = 0;
+ dir.permille = options->dirstat_permille;
+ dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+
+ changed = 0;
+ for (i = 0; i < data->nr; i++) {
+ struct diffstat_file *file = data->files[i];
+ unsigned long damage = file->added + file->deleted;
+ if (file->is_binary)
+ /*
+ * binary files counts bytes, not lines. Must find some
+ * way to normalize binary bytes vs. textual lines.
+ * The following heuristic assumes that there are 64
+ * bytes per "line".
+ * This is stupid and ugly, but very cheap...
+ */
+ damage = (damage + 63) / 64;
+ ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
+ dir.files[dir.nr].name = file->name;
+ dir.files[dir.nr].changed = damage;
+ changed += damage;
+ dir.nr++;
+ }
+
+ /* This can happen even with many files, if everything was renames */
+ if (!changed)
+ return;
+
+ /* Show all directories with more than x% of the changes */
+ qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
+ gather_dirstat(options, &dir, changed, "", 0);
+}
+
static void free_diffstat_info(struct diffstat_t *diffstat)
{
int i;
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
- options->dirstat_percent = 3;
+ options->dirstat_permille = diff_dirstat_permille_default;
options->context = 3;
options->change = diff_change;
static int parse_dirstat_opt(struct diff_options *options, const char *params)
{
- if (parse_dirstat_params(options, params))
- die("Failed to parse --dirstat/-X option parameter");
+ struct strbuf errmsg = STRBUF_INIT;
+ if (parse_dirstat_params(options, params, &errmsg))
+ die(_("Failed to parse --dirstat/-X option parameter:\n%s"),
+ errmsg.buf);
+ strbuf_release(&errmsg);
/*
* The caller knows a dirstat-related option is given from the command
* line; allow it to say "return this_function();"
struct diff_queue_struct *q = &diff_queued_diff;
int i, output_format = options->output_format;
int separator = 0;
+ int dirstat_by_line = 0;
/*
* Order: raw, stat, summary, patch
separator++;
}
- if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
+ if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
+ dirstat_by_line = 1;
+
+ if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
+ dirstat_by_line) {
struct diffstat_t diffstat;
memset(&diffstat, 0, sizeof(struct diffstat_t));
show_stats(&diffstat, options);
if (output_format & DIFF_FORMAT_SHORTSTAT)
show_shortstats(&diffstat, options);
+ if (output_format & DIFF_FORMAT_DIRSTAT)
+ show_dirstat_by_line(&diffstat, options);
free_diffstat_info(&diffstat);
separator++;
}
- if (output_format & DIFF_FORMAT_DIRSTAT)
+ if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)
show_dirstat(options);
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
return result;
}
+int diff_can_quit_early(struct diff_options *opt)
+{
+ return (DIFF_OPT_TST(opt, QUICK) &&
+ !opt->filter &&
+ DIFF_OPT_TST(opt, HAS_CHANGES));
+}
+
/*
* Shall changes to this submodule be ignored?
*
DIFF_OPT_SET(options, HAS_CHANGES);
}
-void diff_unmerge(struct diff_options *options,
- const char *path,
- unsigned mode, const unsigned char *sha1)
+struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
{
+ struct diff_filepair *pair;
struct diff_filespec *one, *two;
if (options->prefix &&
strncmp(path, options->prefix, options->prefix_length))
- return;
+ return NULL;
one = alloc_filespec(path);
two = alloc_filespec(path);
- fill_filespec(one, sha1, mode);
- diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
+ pair = diff_queue(&diff_queued_diff, one, two);
+ pair->is_unmerged = 1;
+ return pair;
}
static char *run_textconv(const char *pgm, struct diff_filespec *spec,