summaryrefslogtreecommitdiff
path: root/diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'diff.c')
-rw-r--r--diff.c266
1 files changed, 190 insertions, 76 deletions
diff --git a/diff.c b/diff.c
index 469e18aed2..ccfa1fca0d 100644
--- a/diff.c
+++ b/diff.c
@@ -1,34 +1,45 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "base85.h"
#include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
#include "tempfile.h"
#include "quote.h"
#include "diff.h"
#include "diffcore.h"
#include "delta.h"
+#include "hex.h"
#include "xdiff-interface.h"
#include "color.h"
-#include "attr.h"
#include "run-command.h"
#include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "userdiff.h"
-#include "submodule-config.h"
#include "submodule.h"
#include "hashmap.h"
#include "mem-pool.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "string-list.h"
#include "strvec.h"
#include "graph.h"
+#include "oid-array.h"
#include "packfile.h"
+#include "pager.h"
#include "parse-options.h"
#include "help.h"
#include "promisor-remote.h"
#include "dir.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "read-cache-ll.h"
+#include "setup.h"
#include "strmap.h"
+#include "ws.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@@ -52,6 +63,7 @@ int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
static int diff_relative;
+static int diff_stat_name_width;
static int diff_stat_graph_width;
static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options;
@@ -127,7 +139,7 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
int i;
if (*params_copy)
- string_list_split_in_place(&params, params_copy, ',', -1);
+ string_list_split_in_place(&params, params_copy, ",", -1);
for (i = 0; i < params.nr; i++) {
const char *p = params.items[i].string;
if (!strcmp(p, "changes")) {
@@ -343,7 +355,8 @@ static unsigned parse_color_moved_ws(const char *arg)
return ret;
}
-int git_diff_ui_config(const char *var, const char *value, void *cb)
+int git_diff_ui_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
diff_use_color_default = git_config_colorbool(var, value);
@@ -357,20 +370,24 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "diff.colormovedws")) {
- unsigned cm = parse_color_moved_ws(value);
+ unsigned cm;
+ if (!value)
+ return config_error_nonbool(var);
+ cm = parse_color_moved_ws(value);
if (cm & COLOR_MOVED_WS_ERROR)
return -1;
diff_color_moved_ws_default = cm;
return 0;
}
if (!strcmp(var, "diff.context")) {
- diff_context_default = git_config_int(var, value);
+ diff_context_default = git_config_int(var, value, ctx->kvi);
if (diff_context_default < 0)
return -1;
return 0;
}
if (!strcmp(var, "diff.interhunkcontext")) {
- diff_interhunk_context_default = git_config_int(var, value);
+ diff_interhunk_context_default = git_config_int(var, value,
+ ctx->kvi);
if (diff_interhunk_context_default < 0)
return -1;
return 0;
@@ -395,8 +412,12 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_relative = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.statnamewidth")) {
+ diff_stat_name_width = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
if (!strcmp(var, "diff.statgraphwidth")) {
- diff_stat_graph_width = git_config_int(var, value);
+ diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "diff.external"))
@@ -406,10 +427,15 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "diff.orderfile"))
return git_config_pathname(&diff_order_file_cfg, var, value);
- if (!strcmp(var, "diff.ignoresubmodules"))
+ if (!strcmp(var, "diff.ignoresubmodules")) {
+ if (!value)
+ return config_error_nonbool(var);
handle_ignore_submodules_arg(&default_diff_options, value);
+ }
if (!strcmp(var, "diff.submodule")) {
+ if (!value)
+ return config_error_nonbool(var);
if (parse_submodule_params(&default_diff_options, value))
warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
value);
@@ -417,24 +443,28 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "diff.algorithm")) {
+ if (!value)
+ return config_error_nonbool(var);
diff_algorithm = parse_algorithm_value(value);
if (diff_algorithm < 0)
- return -1;
+ return error(_("unknown value for config '%s': %s"),
+ var, value);
return 0;
}
if (git_color_config(var, value, cb) < 0)
return -1;
- return git_diff_basic_config(var, value, cb);
+ return git_diff_basic_config(var, value, ctx, cb);
}
-int git_diff_basic_config(const char *var, const char *value, void *cb)
+int git_diff_basic_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
const char *name;
if (!strcmp(var, "diff.renamelimit")) {
- diff_rename_limit_default = git_config_int(var, value);
+ diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
return 0;
}
@@ -452,9 +482,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "diff.wserrorhighlight")) {
- int val = parse_ws_error_highlight(value);
+ int val;
+ if (!value)
+ return config_error_nonbool(var);
+ val = parse_ws_error_highlight(value);
if (val < 0)
- return -1;
+ return error(_("unknown value for config '%s': %s"),
+ var, value);
ws_error_highlight_default = val;
return 0;
}
@@ -469,6 +503,8 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "diff.dirstat")) {
struct strbuf errmsg = STRBUF_INIT;
+ if (!value)
+ return config_error_nonbool(var);
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"),
@@ -481,7 +517,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
static char *quote_two(const char *one, const char *two)
@@ -2688,12 +2724,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
number_width = decimal_width(max_change) > number_width ?
decimal_width(max_change) : number_width;
+ if (options->stat_name_width == -1)
+ options->stat_name_width = diff_stat_name_width;
if (options->stat_graph_width == -1)
options->stat_graph_width = diff_stat_graph_width;
/*
- * Guarantee 3/8*16==6 for the graph part
- * and 5/8*16==10 for the filename part
+ * Guarantee 3/8*16 == 6 for the graph part
+ * and 5/8*16 == 10 for the filename part
*/
if (width < 16 + 6 + number_width)
width = 16 + 6 + number_width;
@@ -2993,6 +3031,24 @@ static int dirstat_compare(const void *_a, const void *_b)
return strcmp(a->name, b->name);
}
+static void conclude_dirstat(struct diff_options *options,
+ struct dirstat_dir *dir,
+ unsigned long changed)
+{
+ struct dirstat_file *to_free = dir->files;
+
+ if (!changed) {
+ /* This can happen even with many files, if everything was renames */
+ ;
+ } else {
+ /* Show all directories with more than x% of the changes */
+ QSORT(dir->files, dir->nr, dirstat_compare);
+ gather_dirstat(options, dir, changed, "", 0);
+ }
+
+ free(to_free);
+}
+
static void show_dirstat(struct diff_options *options)
{
int i;
@@ -3082,13 +3138,7 @@ found_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, dirstat_compare);
- gather_dirstat(options, &dir, changed, "", 0);
+ conclude_dirstat(options, &dir, changed);
}
static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
@@ -3126,13 +3176,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
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, dirstat_compare);
- gather_dirstat(options, &dir, changed, "", 0);
+ conclude_dirstat(options, &dir, changed);
}
static void free_diffstat_file(struct diffstat_file *f)
@@ -3374,6 +3418,17 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
options->b_prefix = b;
}
+void diff_set_noprefix(struct diff_options *options)
+{
+ options->a_prefix = options->b_prefix = "";
+}
+
+void diff_set_default_prefix(struct diff_options *options)
+{
+ options->a_prefix = "a/";
+ options->b_prefix = "b/";
+}
+
struct userdiff_driver *get_textconv(struct repository *r,
struct diff_filespec *one)
{
@@ -3530,18 +3585,21 @@ static void builtin_diff(const char *name_a,
strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
if (xfrm_msg)
strbuf_addstr(&header, xfrm_msg);
+ o->found_changes = 1;
must_show_header = 1;
}
else if (lbl[1][0] == '/') {
strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
if (xfrm_msg)
strbuf_addstr(&header, xfrm_msg);
+ o->found_changes = 1;
must_show_header = 1;
}
else {
if (one->mode != two->mode) {
strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset);
strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset);
+ o->found_changes = 1;
must_show_header = 1;
}
if (xfrm_msg)
@@ -4326,7 +4384,8 @@ static void run_external_diff(const char *pgm,
add_external_diff_name(o->repo, &cmd.args, two);
if (other) {
strvec_push(&cmd.args, other);
- strvec_push(&cmd.args, xfrm_msg);
+ if (xfrm_msg)
+ strvec_push(&cmd.args, xfrm_msg);
}
}
@@ -4351,7 +4410,7 @@ static int similarity_index(struct diff_filepair *p)
static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
{
if (startup_info->have_repository)
- return find_unique_abbrev(oid, abbrev);
+ return repo_find_unique_abbrev(the_repository, oid, abbrev);
else {
char *hex = oid_to_hex(oid);
if (abbrev < 0)
@@ -4674,10 +4733,9 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->flags.ignore_untracked_in_submodules = 1;
if (diff_no_prefix) {
- options->a_prefix = options->b_prefix = "";
+ diff_set_noprefix(options);
} else if (!diff_mnemonic_prefix) {
- options->a_prefix = "a/";
- options->b_prefix = "b/";
+ diff_set_default_prefix(options);
}
options->color_moved = diff_color_moved_default;
@@ -4721,6 +4779,31 @@ unsigned diff_filter_bit(char status)
return filter_bit[(int) status];
}
+int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error)
+{
+ unsigned forbidden_magic;
+
+ if (ps->nr != 1) {
+ if (die_on_error)
+ die(_("--follow requires exactly one pathspec"));
+ return 0;
+ }
+
+ forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP |
+ PATHSPEC_LITERAL);
+ if (forbidden_magic) {
+ if (die_on_error) {
+ struct strbuf sb = STRBUF_INIT;
+ pathspec_magic_names(forbidden_magic, &sb);
+ die(_("pathspec magic not supported by --follow: %s"),
+ sb.buf);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
void diff_setup_done(struct diff_options *options)
{
unsigned check_mask = DIFF_FORMAT_NAME |
@@ -4775,6 +4858,10 @@ void diff_setup_done(struct diff_options *options)
else
options->prefix_length = 0;
+ /*
+ * --name-only, --name-status, --checkdiff, and -s
+ * turn other output format off.
+ */
if (options->output_format & (DIFF_FORMAT_NAME |
DIFF_FORMAT_NAME_STATUS |
DIFF_FORMAT_CHECKDIFF |
@@ -4828,8 +4915,8 @@ void diff_setup_done(struct diff_options *options)
options->diff_path_counter = 0;
- if (options->flags.follow_renames && options->pathspec.nr != 1)
- die(_("--follow requires exactly one pathspec"));
+ if (options->flags.follow_renames)
+ diff_check_follow_pathspec(&options->pathspec, 1);
if (!options->use_color || external_diff())
options->color_moved = 0;
@@ -4906,6 +4993,7 @@ static int diff_opt_stat(const struct option *opt, const char *value, int unset)
} else
BUG("%s should not get here", opt->long_name);
+ options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIFFSTAT;
options->stat_name_width = name_width;
options->stat_graph_width = graph_width;
@@ -4925,6 +5013,7 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
* 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_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIRSTAT;
return 1;
}
@@ -4988,7 +5077,7 @@ static int diff_opt_find_object(const struct option *option,
struct object_id oid;
BUG_ON_OPT_NEG(unset);
- if (get_oid(arg, &oid))
+ if (repo_get_oid(the_repository, arg, &oid))
return error(_("unable to resolve '%s'"), arg);
if (!opt->objfind)
@@ -5124,6 +5213,7 @@ static int diff_opt_compact_summary(const struct option *opt,
options->flags.stat_with_summary = 0;
} else {
options->flags.stat_with_summary = 1;
+ options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIFFSTAT;
}
return 0;
@@ -5261,8 +5351,18 @@ static int diff_opt_no_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
- options->a_prefix = "";
- options->b_prefix = "";
+ diff_set_noprefix(options);
+ return 0;
+}
+
+static int diff_opt_default_prefix(const struct option *opt,
+ const char *optarg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(optarg);
+ diff_set_default_prefix(options);
return 0;
}
@@ -5451,6 +5551,10 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns
return 0;
}
+/*
+ * Consider adding new flags to __git_diff_common_options
+ * in contrib/completion/git-completion.bash
+ */
struct option *add_diff_options(const struct option *opts,
struct diff_options *options)
{
@@ -5459,9 +5563,8 @@ struct option *add_diff_options(const struct option *opts,
OPT_BITOP('p', "patch", &options->output_format,
N_("generate patch"),
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
- OPT_BIT_F('s', "no-patch", &options->output_format,
- N_("suppress diff output"),
- DIFF_FORMAT_NO_OUTPUT, PARSE_OPT_NONEG),
+ OPT_SET_INT('s', "no-patch", &options->output_format,
+ N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT),
OPT_BITOP('u', NULL, &options->output_format,
N_("generate patch"),
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
@@ -5470,9 +5573,9 @@ struct option *add_diff_options(const struct option *opts,
PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified),
OPT_BOOL('W', "function-context", &options->flags.funccontext,
N_("generate diffs with <n> lines context")),
- OPT_BIT_F(0, "raw", &options->output_format,
+ OPT_BITOP(0, "raw", &options->output_format,
N_("generate the diff in raw format"),
- DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
+ DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT),
OPT_BITOP(0, "patch-with-raw", &options->output_format,
N_("synonym for '-p --raw'"),
DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW,
@@ -5481,12 +5584,12 @@ struct option *add_diff_options(const struct option *opts,
N_("synonym for '-p --stat'"),
DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT,
DIFF_FORMAT_NO_OUTPUT),
- OPT_BIT_F(0, "numstat", &options->output_format,
+ OPT_BITOP(0, "numstat", &options->output_format,
N_("machine friendly --stat"),
- DIFF_FORMAT_NUMSTAT, PARSE_OPT_NONEG),
- OPT_BIT_F(0, "shortstat", &options->output_format,
+ DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT),
+ OPT_BITOP(0, "shortstat", &options->output_format,
N_("output only the last line of --stat"),
- DIFF_FORMAT_SHORTSTAT, PARSE_OPT_NONEG),
+ DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
N_("output the distribution of relative amount of changes for each sub-directory"),
PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
@@ -5502,9 +5605,9 @@ struct option *add_diff_options(const struct option *opts,
OPT_BIT_F(0, "check", &options->output_format,
N_("warn if changes introduce conflict markers or whitespace errors"),
DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG),
- OPT_BIT_F(0, "summary", &options->output_format,
+ OPT_BITOP(0, "summary", &options->output_format,
N_("condensed summary such as creations, renames and mode changes"),
- DIFF_FORMAT_SUMMARY, PARSE_OPT_NONEG),
+ DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT),
OPT_BIT_F(0, "name-only", &options->output_format,
N_("show only names of changed files"),
DIFF_FORMAT_NAME, PARSE_OPT_NONEG),
@@ -5555,6 +5658,9 @@ struct option *add_diff_options(const struct option *opts,
OPT_CALLBACK_F(0, "no-prefix", options, NULL,
N_("do not show any source or destination prefix"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
+ OPT_CALLBACK_F(0, "default-prefix", options, NULL,
+ N_("use default prefixes a/ and b/"),
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix),
OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
N_("show context between diff hunks up to the specified number of lines"),
PARSE_OPT_NONEG),
@@ -6130,6 +6236,8 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
fprintf(opt->file, "%s", diff_line_prefix(opt));
write_name_quoted(name_a, opt->file, opt->line_termination);
}
+
+ opt->found_changes = 1;
}
static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
@@ -6608,6 +6716,21 @@ void diff_flush(struct diff_options *options)
separator++;
}
+ if (output_format & DIFF_FORMAT_PATCH) {
+ if (separator) {
+ emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
+ if (options->stat_sep)
+ /* attach patch instead of inline */
+ emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
+ NULL, 0, 0);
+ }
+
+ diff_flush_patch_all_file_pairs(options);
+ }
+
+ if (output_format & DIFF_FORMAT_CALLBACK)
+ options->format_callback(q, options, options->format_callback_data);
+
if (output_format & DIFF_FORMAT_NO_OUTPUT &&
options->flags.exit_with_status &&
options->flags.diff_from_contents) {
@@ -6629,21 +6752,6 @@ void diff_flush(struct diff_options *options)
}
}
- if (output_format & DIFF_FORMAT_PATCH) {
- if (separator) {
- emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
- if (options->stat_sep)
- /* attach patch instead of inline */
- emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
- NULL, 0, 0);
- }
-
- diff_flush_patch_all_file_pairs(options);
- }
-
- if (output_format & DIFF_FORMAT_CALLBACK)
- options->format_callback(q, options, options->format_callback_data);
-
free_queue:
diff_free_queue(q);
DIFF_QUEUE_CLEAR(q);
@@ -6844,6 +6952,13 @@ void diff_queued_diff_prefetch(void *repository)
oid_array_clear(&to_fetch);
}
+void init_diffstat_widths(struct diff_options *options)
+{
+ options->stat_width = -1; /* use full terminal width */
+ options->stat_name_width = -1; /* respect diff.statNameWidth config */
+ options->stat_graph_width = -1; /* respect diff.statGraphWidth config */
+}
+
void diffcore_std(struct diff_options *options)
{
int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT |
@@ -6860,7 +6975,7 @@ void diffcore_std(struct diff_options *options)
* If no prefetching occurs, diffcore_rename() will prefetch if it
* decides that it needs inexact rename detection.
*/
- if (options->repo == the_repository && has_promisor_remote() &&
+ if (options->repo == the_repository && repo_has_promisor_remote(the_repository) &&
(options->output_format & output_formats_to_prefetch ||
options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
diff_queued_diff_prefetch(options->repo);
@@ -6897,16 +7012,14 @@ void diffcore_std(struct diff_options *options)
options->found_follow = 0;
}
-int diff_result_code(struct diff_options *opt, int status)
+int diff_result_code(struct diff_options *opt)
{
int result = 0;
diff_warn_rename_limit("diff.renameLimit",
opt->needed_rename_limit,
opt->degraded_cc_to_c);
- if (!opt->flags.exit_with_status &&
- !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
- return status;
+
if (opt->flags.exit_with_status &&
opt->flags.has_changes)
result |= 01;
@@ -6953,6 +7066,7 @@ void compute_diffstat(struct diff_options *options,
if (check_pair_status(p))
diff_flush_stat(p, options, diffstat);
}
+ options->found_changes = !!diffstat->nr;
}
void diff_addremove(struct diff_options *options,