Merge branch 'jh/dirstat-lines'

* jh/dirstat-lines:
  Mark dirstat error messages for translation
  Improve error handling when parsing dirstat parameters
  New --dirstat=lines mode, doing dirstat analysis based on diffstat
  Allow specifying --dirstat cut-off percentage as a floating point number
  Add config variable for specifying default --dirstat behavior
  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file
  Make --dirstat=0 output directories that contribute < 0.1% of changes
  Add several testcases for --dirstat and friends
maint
Junio C Hamano 2011-05-13 11:01:32 -07:00
commit df54e2bfd6
5 changed files with 1219 additions and 32 deletions

View File

@ -8,6 +8,50 @@ diff.autorefreshindex::
affects only 'git diff' Porcelain, and not lower level affects only 'git diff' Porcelain, and not lower level
'diff' commands such as 'git diff-files'. 'diff' commands such as 'git diff-files'.


diff.dirstat::
A comma separated list of `--dirstat` parameters specifying the
default behavior of the `--dirstat` option to linkgit:git-diff[1]`
and friends. The defaults can be overridden on the command line
(using `--dirstat=<param1,param2,...>`). The fallback defaults
(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.
The following parameters are available:
+
--
`changes`;;
Compute the dirstat numbers by counting the lines that have been
removed from the source, or added to the destination. This ignores
the amount of pure code movements within a file. In other words,
rearranging lines in a file is not counted as much as other changes.
This is the default behavior when no parameter is given.
`lines`;;
Compute the dirstat numbers by doing the regular line-based diff
analysis, and summing the removed/added line counts. (For binary
files, count 64-byte chunks instead, since binary files have no
natural concept of lines). This is a more expensive `--dirstat`
behavior than the `changes` behavior, but it does count rearranged
lines within a file as much as other changes. The resulting output
is consistent with what you get from the other `--*stat` options.
`files`;;
Compute the dirstat numbers by counting the number of files changed.
Each changed file counts equally in the dirstat analysis. This is
the computationally cheapest `--dirstat` behavior, since it does
not have to look at the file contents at all.
`cumulative`;;
Count changes in a child directory for the parent directory as well.
Note that when using `cumulative`, the sum of the percentages
reported may exceed 100%. The default (non-cumulative) behavior can
be specified with the `noncumulative` parameter.
<limit>;;
An integer parameter specifies a cut-off percent (3% by default).
Directories contributing less than this percentage of the changes
are not shown in the output.
--
+
Example: The following will count changed files, while ignoring
directories with less than 10% of the total amount of changed files,
and accumulating child directory counts in the parent directories:
`files,10,cumulative`.

diff.external:: diff.external::
If this config variable is set, diff generation is not If this config variable is set, diff generation is not
performed using the internal diff machinery, but using the performed using the internal diff machinery, but using the

View File

@ -66,19 +66,49 @@ endif::git-format-patch[]
number of modified files, as well as number of added and deleted number of modified files, as well as number of added and deleted
lines. lines.


--dirstat[=<limit>]:: --dirstat[=<param1,param2,...>]::
Output the distribution of relative amount of changes (number of lines added or Output the distribution of relative amount of changes for each
removed) for each sub-directory. Directories with changes below sub-directory. The behavior of `--dirstat` can be customized by
a cut-off percent (3% by default) are not shown. The cut-off percent passing it a comma separated list of parameters.
can be set with `--dirstat=<limit>`. Changes in a child directory are not The defaults are controlled by the `diff.dirstat` configuration
counted for the parent directory, unless `--cumulative` is used. variable (see linkgit:git-config[1]).
The following parameters are available:
+ +
Note that the `--dirstat` option computes the changes while ignoring --
the amount of pure code movements within a file. In other words, `changes`;;
rearranging lines in a file is not counted as much as other changes. Compute the dirstat numbers by counting the lines that have been

removed from the source, or added to the destination. This ignores
--dirstat-by-file[=<limit>]:: the amount of pure code movements within a file. In other words,
Same as `--dirstat`, but counts changed files instead of lines. rearranging lines in a file is not counted as much as other changes.
This is the default behavior when no parameter is given.
`lines`;;
Compute the dirstat numbers by doing the regular line-based diff
analysis, and summing the removed/added line counts. (For binary
files, count 64-byte chunks instead, since binary files have no
natural concept of lines). This is a more expensive `--dirstat`
behavior than the `changes` behavior, but it does count rearranged
lines within a file as much as other changes. The resulting output
is consistent with what you get from the other `--*stat` options.
`files`;;
Compute the dirstat numbers by counting the number of files changed.
Each changed file counts equally in the dirstat analysis. This is
the computationally cheapest `--dirstat` behavior, since it does
not have to look at the file contents at all.
`cumulative`;;
Count changes in a child directory for the parent directory as well.
Note that when using `cumulative`, the sum of the percentages
reported may exceed 100%. The default (non-cumulative) behavior can
be specified with the `noncumulative` parameter.
<limit>;;
An integer parameter specifies a cut-off percent (3% by default).
Directories contributing less than this percentage of the changes
are not shown in the output.
--
+
Example: The following will count changed files, while ignoring
directories with less than 10% of the total amount of changed files,
and accumulating child directory counts in the parent directories:
`--dirstat=files,10,cumulative`.


--summary:: --summary::
Output a condensed summary of extended header information Output a condensed summary of extended header information

171
diff.c
View File

@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1; int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix; static int diff_mnemonic_prefix;
static int diff_no_prefix; static int diff_no_prefix;
static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options; static struct diff_options default_diff_options;


static char diff_colors[][COLOR_MAXLEN] = { static char diff_colors[][COLOR_MAXLEN] = {
@ -66,6 +67,58 @@ static int parse_diff_color_slot(const char *var, int ofs)
return -1; return -1;
} }


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) {
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 (!memcmp(p, "files", p_len)) {
DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
} else if (!memcmp(p, "noncumulative", p_len)) {
DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
} else if (!memcmp(p, "cumulative", p_len)) {
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
} else if (isdigit(*p)) {
char *end;
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 ret;
}

static int git_config_rename(const char *var, const char *value) static int git_config_rename(const char *var, const char *value)
{ {
if (!value) if (!value)
@ -145,6 +198,17 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return 0; 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.")) if (!prefixcmp(var, "submodule."))
return parse_submodule_config_option(var, value); return parse_submodule_config_option(var, value);


@ -1455,7 +1519,7 @@ struct dirstat_file {


struct dirstat_dir { struct dirstat_dir {
struct dirstat_file *files; 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, static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
@ -1502,12 +1566,11 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
* under this directory (sources == 1). * under this directory (sources == 1).
*/ */
if (baselen && sources != 1) { if (baselen && sources != 1) {
int permille = this_dir * 1000 / changed; if (this_dir) {
if (permille) { int permille = this_dir * 1000 / changed;
int percent = permille / 10; if (permille >= dir->permille) {
if (percent >= dir->percent) {
fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix, fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
percent, permille % 10, baselen, base); permille / 10, permille % 10, baselen, base);
if (!dir->cumulative) if (!dir->cumulative)
return 0; return 0;
} }
@ -1533,7 +1596,7 @@ static void show_dirstat(struct diff_options *options)
dir.files = NULL; dir.files = NULL;
dir.alloc = 0; dir.alloc = 0;
dir.nr = 0; dir.nr = 0;
dir.percent = options->dirstat_percent; dir.permille = options->dirstat_permille;
dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE); dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);


changed = 0; changed = 0;
@ -1622,6 +1685,50 @@ found_damage:
gather_dirstat(options, &dir, 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) static void free_diffstat_info(struct diffstat_t *diffstat)
{ {
int i; int i;
@ -2891,7 +2998,7 @@ void diff_setup(struct diff_options *options)
options->line_termination = '\n'; options->line_termination = '\n';
options->break_opt = -1; options->break_opt = -1;
options->rename_limit = -1; options->rename_limit = -1;
options->dirstat_percent = 3; options->dirstat_permille = diff_dirstat_permille_default;
options->context = 3; options->context = 3;


options->change = diff_change; options->change = diff_change;
@ -3149,6 +3256,21 @@ static int stat_opt(struct diff_options *options, const char **av)
return argcount; return argcount;
} }


static int parse_dirstat_opt(struct diff_options *options, const char *params)
{
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();"
*/
options->output_format |= DIFF_FORMAT_DIRSTAT;
return 1;
}

int diff_opt_parse(struct diff_options *options, const char **av, int ac) int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{ {
const char *arg = av[0]; const char *arg = av[0];
@ -3168,15 +3290,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->output_format |= DIFF_FORMAT_NUMSTAT; options->output_format |= DIFF_FORMAT_NUMSTAT;
else if (!strcmp(arg, "--shortstat")) else if (!strcmp(arg, "--shortstat"))
options->output_format |= DIFF_FORMAT_SHORTSTAT; options->output_format |= DIFF_FORMAT_SHORTSTAT;
else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent)) else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
options->output_format |= DIFF_FORMAT_DIRSTAT; return parse_dirstat_opt(options, "");
else if (!strcmp(arg, "--cumulative")) { else if (!prefixcmp(arg, "-X"))
options->output_format |= DIFF_FORMAT_DIRSTAT; return parse_dirstat_opt(options, arg + 2);
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE); else if (!prefixcmp(arg, "--dirstat="))
} else if (opt_arg(arg, 0, "dirstat-by-file", return parse_dirstat_opt(options, arg + 10);
&options->dirstat_percent)) { else if (!strcmp(arg, "--cumulative"))
options->output_format |= DIFF_FORMAT_DIRSTAT; return parse_dirstat_opt(options, "cumulative");
DIFF_OPT_SET(options, DIRSTAT_BY_FILE); else if (!strcmp(arg, "--dirstat-by-file"))
return parse_dirstat_opt(options, "files");
else if (!prefixcmp(arg, "--dirstat-by-file=")) {
parse_dirstat_opt(options, "files");
return parse_dirstat_opt(options, arg + 18);
} }
else if (!strcmp(arg, "--check")) else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF; options->output_format |= DIFF_FORMAT_CHECKDIFF;
@ -4023,6 +4149,7 @@ void diff_flush(struct diff_options *options)
struct diff_queue_struct *q = &diff_queued_diff; struct diff_queue_struct *q = &diff_queued_diff;
int i, output_format = options->output_format; int i, output_format = options->output_format;
int separator = 0; int separator = 0;
int dirstat_by_line = 0;


/* /*
* Order: raw, stat, summary, patch * Order: raw, stat, summary, patch
@ -4043,7 +4170,11 @@ void diff_flush(struct diff_options *options)
separator++; 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; struct diffstat_t diffstat;


memset(&diffstat, 0, sizeof(struct diffstat_t)); memset(&diffstat, 0, sizeof(struct diffstat_t));
@ -4058,10 +4189,12 @@ void diff_flush(struct diff_options *options)
show_stats(&diffstat, options); show_stats(&diffstat, options);
if (output_format & DIFF_FORMAT_SHORTSTAT) if (output_format & DIFF_FORMAT_SHORTSTAT)
show_shortstats(&diffstat, options); show_shortstats(&diffstat, options);
if (output_format & DIFF_FORMAT_DIRSTAT)
show_dirstat_by_line(&diffstat, options);
free_diffstat_info(&diffstat); free_diffstat_info(&diffstat);
separator++; separator++;
} }
if (output_format & DIFF_FORMAT_DIRSTAT) if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)
show_dirstat(options); show_dirstat(options);


if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {

3
diff.h
View File

@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25) #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26) #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27) #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
#define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28)


#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
@ -114,7 +115,7 @@ struct diff_options {
int needed_rename_limit; int needed_rename_limit;
int degraded_cc_to_c; int degraded_cc_to_c;
int show_rename_progress; int show_rename_progress;
int dirstat_percent; int dirstat_permille;
int setup; int setup;
int abbrev; int abbrev;
const char *prefix; const char *prefix;

979
t/t4047-diff-dirstat.sh Executable file
View File

@ -0,0 +1,979 @@
#!/bin/sh

test_description='diff --dirstat tests'
. ./test-lib.sh

# set up two commits where the second commit has these files
# (10 lines in each file):
#
# unchanged/text (unchanged from 1st commit)
# changed/text (changed 1st line)
# rearranged/text (swapped 1st and 2nd line)
# dst/copy/unchanged/text (copied from src/copy/unchanged/text, unchanged)
# dst/copy/changed/text (copied from src/copy/changed/text, changed)
# dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)
# dst/move/unchanged/text (moved from src/move/unchanged/text, unchanged)
# dst/move/changed/text (moved from src/move/changed/text, changed)
# dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)

test_expect_success 'setup' '
mkdir unchanged &&
mkdir changed &&
mkdir rearranged &&
mkdir src &&
mkdir src/copy &&
mkdir src/copy/unchanged &&
mkdir src/copy/changed &&
mkdir src/copy/rearranged &&
mkdir src/move &&
mkdir src/move/unchanged &&
mkdir src/move/changed &&
mkdir src/move/rearranged &&
cat <<EOF >unchanged/text &&
unchanged line #0
unchanged line #1
unchanged line #2
unchanged line #3
unchanged line #4
unchanged line #5
unchanged line #6
unchanged line #7
unchanged line #8
unchanged line #9
EOF
cat <<EOF >changed/text &&
changed line #0
changed line #1
changed line #2
changed line #3
changed line #4
changed line #5
changed line #6
changed line #7
changed line #8
changed line #9
EOF
cat <<EOF >rearranged/text &&
rearranged line #0
rearranged line #1
rearranged line #2
rearranged line #3
rearranged line #4
rearranged line #5
rearranged line #6
rearranged line #7
rearranged line #8
rearranged line #9
EOF
cat <<EOF >src/copy/unchanged/text &&
copy unchanged line #0
copy unchanged line #1
copy unchanged line #2
copy unchanged line #3
copy unchanged line #4
copy unchanged line #5
copy unchanged line #6
copy unchanged line #7
copy unchanged line #8
copy unchanged line #9
EOF
cat <<EOF >src/copy/changed/text &&
copy changed line #0
copy changed line #1
copy changed line #2
copy changed line #3
copy changed line #4
copy changed line #5
copy changed line #6
copy changed line #7
copy changed line #8
copy changed line #9
EOF
cat <<EOF >src/copy/rearranged/text &&
copy rearranged line #0
copy rearranged line #1
copy rearranged line #2
copy rearranged line #3
copy rearranged line #4
copy rearranged line #5
copy rearranged line #6
copy rearranged line #7
copy rearranged line #8
copy rearranged line #9
EOF
cat <<EOF >src/move/unchanged/text &&
move unchanged line #0
move unchanged line #1
move unchanged line #2
move unchanged line #3
move unchanged line #4
move unchanged line #5
move unchanged line #6
move unchanged line #7
move unchanged line #8
move unchanged line #9
EOF
cat <<EOF >src/move/changed/text &&
move changed line #0
move changed line #1
move changed line #2
move changed line #3
move changed line #4
move changed line #5
move changed line #6
move changed line #7
move changed line #8
move changed line #9
EOF
cat <<EOF >src/move/rearranged/text &&
move rearranged line #0
move rearranged line #1
move rearranged line #2
move rearranged line #3
move rearranged line #4
move rearranged line #5
move rearranged line #6
move rearranged line #7
move rearranged line #8
move rearranged line #9
EOF
git add . &&
git commit -m "initial" &&
mkdir dst &&
mkdir dst/copy &&
mkdir dst/copy/unchanged &&
mkdir dst/copy/changed &&
mkdir dst/copy/rearranged &&
mkdir dst/move &&
mkdir dst/move/unchanged &&
mkdir dst/move/changed &&
mkdir dst/move/rearranged &&
cat <<EOF >changed/text &&
CHANGED XXXXXXX line #0
changed line #1
changed line #2
changed line #3
changed line #4
changed line #5
changed line #6
changed line #7
changed line #8
changed line #9
EOF
cat <<EOF >rearranged/text &&
rearranged line #1
rearranged line #0
rearranged line #2
rearranged line #3
rearranged line #4
rearranged line #5
rearranged line #6
rearranged line #7
rearranged line #8
rearranged line #9
EOF
cat <<EOF >dst/copy/unchanged/text &&
copy unchanged line #0
copy unchanged line #1
copy unchanged line #2
copy unchanged line #3
copy unchanged line #4
copy unchanged line #5
copy unchanged line #6
copy unchanged line #7
copy unchanged line #8
copy unchanged line #9
EOF
cat <<EOF >dst/copy/changed/text &&
copy XXXCHANGED line #0
copy changed line #1
copy changed line #2
copy changed line #3
copy changed line #4
copy changed line #5
copy changed line #6
copy changed line #7
copy changed line #8
copy changed line #9
EOF
cat <<EOF >dst/copy/rearranged/text &&
copy rearranged line #1
copy rearranged line #0
copy rearranged line #2
copy rearranged line #3
copy rearranged line #4
copy rearranged line #5
copy rearranged line #6
copy rearranged line #7
copy rearranged line #8
copy rearranged line #9
EOF
cat <<EOF >dst/move/unchanged/text &&
move unchanged line #0
move unchanged line #1
move unchanged line #2
move unchanged line #3
move unchanged line #4
move unchanged line #5
move unchanged line #6
move unchanged line #7
move unchanged line #8
move unchanged line #9
EOF
cat <<EOF >dst/move/changed/text &&
move XXXCHANGED line #0
move changed line #1
move changed line #2
move changed line #3
move changed line #4
move changed line #5
move changed line #6
move changed line #7
move changed line #8
move changed line #9
EOF
cat <<EOF >dst/move/rearranged/text &&
move rearranged line #1
move rearranged line #0
move rearranged line #2
move rearranged line #3
move rearranged line #4
move rearranged line #5
move rearranged line #6
move rearranged line #7
move rearranged line #8
move rearranged line #9
EOF
git add . &&
git rm -r src/move/unchanged &&
git rm -r src/move/changed &&
git rm -r src/move/rearranged &&
git commit -m "changes"
'

cat <<EOF >expect_diff_stat
changed/text | 2 +-
dst/copy/changed/text | 10 ++++++++++
dst/copy/rearranged/text | 10 ++++++++++
dst/copy/unchanged/text | 10 ++++++++++
dst/move/changed/text | 10 ++++++++++
dst/move/rearranged/text | 10 ++++++++++
dst/move/unchanged/text | 10 ++++++++++
rearranged/text | 2 +-
src/move/changed/text | 10 ----------
src/move/rearranged/text | 10 ----------
src/move/unchanged/text | 10 ----------
11 files changed, 62 insertions(+), 32 deletions(-)
EOF

cat <<EOF >expect_diff_stat_M
changed/text | 2 +-
dst/copy/changed/text | 10 ++++++++++
dst/copy/rearranged/text | 10 ++++++++++
dst/copy/unchanged/text | 10 ++++++++++
{src => dst}/move/changed/text | 2 +-
{src => dst}/move/rearranged/text | 2 +-
{src => dst}/move/unchanged/text | 0
rearranged/text | 2 +-
8 files changed, 34 insertions(+), 4 deletions(-)
EOF

cat <<EOF >expect_diff_stat_CC
changed/text | 2 +-
{src => dst}/copy/changed/text | 2 +-
{src => dst}/copy/rearranged/text | 2 +-
{src => dst}/copy/unchanged/text | 0
{src => dst}/move/changed/text | 2 +-
{src => dst}/move/rearranged/text | 2 +-
{src => dst}/move/unchanged/text | 0
rearranged/text | 2 +-
8 files changed, 6 insertions(+), 6 deletions(-)
EOF

test_expect_success 'sanity check setup (--stat)' '
git diff --stat HEAD^..HEAD >actual_diff_stat &&
test_cmp expect_diff_stat actual_diff_stat &&
git diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&
test_cmp expect_diff_stat_M actual_diff_stat_M &&
git diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&
test_cmp expect_diff_stat_CC actual_diff_stat_CC
'

# changed/text and rearranged/text falls below default 3% threshold
cat <<EOF >expect_diff_dirstat
10.8% dst/copy/changed/
10.8% dst/copy/rearranged/
10.8% dst/copy/unchanged/
10.8% dst/move/changed/
10.8% dst/move/rearranged/
10.8% dst/move/unchanged/
10.8% src/move/changed/
10.8% src/move/rearranged/
10.8% src/move/unchanged/
EOF

# rearranged/text falls below default 3% threshold
cat <<EOF >expect_diff_dirstat_M
5.8% changed/
29.3% dst/copy/changed/
29.3% dst/copy/rearranged/
29.3% dst/copy/unchanged/
5.8% dst/move/changed/
EOF

# rearranged/text falls below default 3% threshold
cat <<EOF >expect_diff_dirstat_CC
32.6% changed/
32.6% dst/copy/changed/
32.6% dst/move/changed/
EOF

test_expect_success 'various ways to misspell --dirstat' '
test_must_fail git show --dirstat10 &&
test_must_fail git show --dirstat10,files &&
test_must_fail git show -X=20 &&
test_must_fail git show -X=20,cumulative
'

test_expect_success 'vanilla --dirstat' '
git diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'vanilla -X' '
git diff -X HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '
git diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '
git diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'later options override earlier options:' '
git diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '
git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
2.1% changed/
10.8% dst/copy/changed/
10.8% dst/copy/rearranged/
10.8% dst/copy/unchanged/
10.8% dst/move/changed/
10.8% dst/move/rearranged/
10.8% dst/move/unchanged/
0.0% rearranged/
10.8% src/move/changed/
10.8% src/move/rearranged/
10.8% src/move/unchanged/
EOF

cat <<EOF >expect_diff_dirstat_M
5.8% changed/
29.3% dst/copy/changed/
29.3% dst/copy/rearranged/
29.3% dst/copy/unchanged/
5.8% dst/move/changed/
0.1% dst/move/rearranged/
0.1% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
32.6% changed/
32.6% dst/copy/changed/
0.6% dst/copy/rearranged/
32.6% dst/move/changed/
0.6% dst/move/rearranged/
0.6% rearranged/
EOF

test_expect_success '--dirstat=0' '
git diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '-X0' '
git diff -X0 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=0' '
git -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
2.1% changed/
10.8% dst/copy/changed/
10.8% dst/copy/rearranged/
10.8% dst/copy/unchanged/
32.5% dst/copy/
10.8% dst/move/changed/
10.8% dst/move/rearranged/
10.8% dst/move/unchanged/
32.5% dst/move/
65.1% dst/
0.0% rearranged/
10.8% src/move/changed/
10.8% src/move/rearranged/
10.8% src/move/unchanged/
32.5% src/move/
EOF

cat <<EOF >expect_diff_dirstat_M
5.8% changed/
29.3% dst/copy/changed/
29.3% dst/copy/rearranged/
29.3% dst/copy/unchanged/
88.0% dst/copy/
5.8% dst/move/changed/
0.1% dst/move/rearranged/
5.9% dst/move/
94.0% dst/
0.1% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
32.6% changed/
32.6% dst/copy/changed/
0.6% dst/copy/rearranged/
33.3% dst/copy/
32.6% dst/move/changed/
0.6% dst/move/rearranged/
33.3% dst/move/
66.6% dst/
0.6% rearranged/
EOF

test_expect_success '--dirstat=0 --cumulative' '
git diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=0,cumulative' '
git diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '-X0,cumulative' '
git diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=0,cumulative' '
git -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '
git -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
9.0% changed/
9.0% dst/copy/changed/
9.0% dst/copy/rearranged/
9.0% dst/copy/unchanged/
9.0% dst/move/changed/
9.0% dst/move/rearranged/
9.0% dst/move/unchanged/
9.0% rearranged/
9.0% src/move/changed/
9.0% src/move/rearranged/
9.0% src/move/unchanged/
EOF

cat <<EOF >expect_diff_dirstat_M
14.2% changed/
14.2% dst/copy/changed/
14.2% dst/copy/rearranged/
14.2% dst/copy/unchanged/
14.2% dst/move/changed/
14.2% dst/move/rearranged/
14.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
16.6% rearranged/
EOF

test_expect_success '--dirstat-by-file' '
git diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=files' '
git diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=files' '
git -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
27.2% dst/copy/
27.2% dst/move/
27.2% src/move/
EOF

cat <<EOF >expect_diff_dirstat_M
14.2% changed/
14.2% dst/copy/changed/
14.2% dst/copy/rearranged/
14.2% dst/copy/unchanged/
14.2% dst/move/changed/
14.2% dst/move/rearranged/
14.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
16.6% rearranged/
EOF

test_expect_success '--dirstat-by-file=10' '
git diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=files,10' '
git diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=10,files' '
git -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
9.0% changed/
9.0% dst/copy/changed/
9.0% dst/copy/rearranged/
9.0% dst/copy/unchanged/
27.2% dst/copy/
9.0% dst/move/changed/
9.0% dst/move/rearranged/
9.0% dst/move/unchanged/
27.2% dst/move/
54.5% dst/
9.0% rearranged/
9.0% src/move/changed/
9.0% src/move/rearranged/
9.0% src/move/unchanged/
27.2% src/move/
EOF

cat <<EOF >expect_diff_dirstat_M
14.2% changed/
14.2% dst/copy/changed/
14.2% dst/copy/rearranged/
14.2% dst/copy/unchanged/
42.8% dst/copy/
14.2% dst/move/changed/
14.2% dst/move/rearranged/
28.5% dst/move/
71.4% dst/
14.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
33.3% dst/copy/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
33.3% dst/move/
66.6% dst/
16.6% rearranged/
EOF

test_expect_success '--dirstat-by-file --cumulative' '
git diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=files,cumulative' '
git diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=cumulative,files' '
git -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
27.2% dst/copy/
27.2% dst/move/
54.5% dst/
27.2% src/move/
EOF

cat <<EOF >expect_diff_dirstat_M
14.2% changed/
14.2% dst/copy/changed/
14.2% dst/copy/rearranged/
14.2% dst/copy/unchanged/
42.8% dst/copy/
14.2% dst/move/changed/
14.2% dst/move/rearranged/
28.5% dst/move/
71.4% dst/
14.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
33.3% dst/copy/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
33.3% dst/move/
66.6% dst/
16.6% rearranged/
EOF

test_expect_success '--dirstat=files,cumulative,10' '
git diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=10,cumulative,files' '
git -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
27.2% dst/copy/
27.2% dst/move/
54.5% dst/
27.2% src/move/
EOF

cat <<EOF >expect_diff_dirstat_M
42.8% dst/copy/
28.5% dst/move/
71.4% dst/
EOF

cat <<EOF >expect_diff_dirstat_CC
33.3% dst/copy/
33.3% dst/move/
66.6% dst/
EOF

test_expect_success '--dirstat=files,cumulative,16.7' '
git diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=16.7,cumulative,files' '
git -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=16.70,cumulative,files' '
git -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=files,cumulative,27.2' '
git diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=files,cumulative,27.09' '
git diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
10.6% dst/copy/changed/
10.6% dst/copy/rearranged/
10.6% dst/copy/unchanged/
10.6% dst/move/changed/
10.6% dst/move/rearranged/
10.6% dst/move/unchanged/
10.6% src/move/changed/
10.6% src/move/rearranged/
10.6% src/move/unchanged/
EOF

cat <<EOF >expect_diff_dirstat_M
5.2% changed/
26.3% dst/copy/changed/
26.3% dst/copy/rearranged/
26.3% dst/copy/unchanged/
5.2% dst/move/changed/
5.2% dst/move/rearranged/
5.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
16.6% rearranged/
EOF

test_expect_success '--dirstat=lines' '
git diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=lines' '
git -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

cat <<EOF >expect_diff_dirstat
2.1% changed/
10.6% dst/copy/changed/
10.6% dst/copy/rearranged/
10.6% dst/copy/unchanged/
10.6% dst/move/changed/
10.6% dst/move/rearranged/
10.6% dst/move/unchanged/
2.1% rearranged/
10.6% src/move/changed/
10.6% src/move/rearranged/
10.6% src/move/unchanged/
EOF

cat <<EOF >expect_diff_dirstat_M
5.2% changed/
26.3% dst/copy/changed/
26.3% dst/copy/rearranged/
26.3% dst/copy/unchanged/
5.2% dst/move/changed/
5.2% dst/move/rearranged/
5.2% rearranged/
EOF

cat <<EOF >expect_diff_dirstat_CC
16.6% changed/
16.6% dst/copy/changed/
16.6% dst/copy/rearranged/
16.6% dst/move/changed/
16.6% dst/move/rearranged/
16.6% rearranged/
EOF

test_expect_success '--dirstat=lines,0' '
git diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success 'diff.dirstat=0,lines' '
git -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
git -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
git -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
'

test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp /dev/null actual_diff_dirstat &&
test_i18ngrep -q "future_param" actual_error &&
test_i18ngrep -q "\--dirstat" actual_error
'

test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp /dev/null actual_diff_dirstat &&
test_i18ngrep -q "dummy1" actual_error &&
test_i18ngrep -q "2dummy" actual_error &&
test_i18ngrep -q "\--dirstat" actual_error
'

test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
test_i18ngrep -q "future_param" actual_error &&
test_i18ngrep -q "diff\\.dirstat" actual_error &&

git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
test_i18ngrep -q "future_param" actual_error &&
test_i18ngrep -q "diff\\.dirstat" actual_error &&

git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
test_i18ngrep -q "future_param" actual_error &&
test_i18ngrep -q "diff\\.dirstat" actual_error
'

test_done