#include "submodule.h"
#include "ll-merge.h"
#include "string-list.h"
+#include "argv-array.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
static int diff_context_default = 3;
static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
+static const char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
return git_config_string(&external_diff_cmd_cfg, var, value);
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
+ if (!strcmp(var, "diff.orderfile"))
+ return git_config_pathname(&diff_order_file_cfg, var, value);
if (!strcmp(var, "diff.ignoresubmodules"))
handle_ignore_submodules_arg(&default_diff_options, value);
if (userdiff_config(var, value) < 0)
return -1;
- if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
+ if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
if (slot < 0)
return 0;
return 0;
}
- if (!prefixcmp(var, "submodule."))
+ if (starts_with(var, "submodule."))
return parse_submodule_config_option(var, value);
return git_default_config(var, value, cb);
diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
- } else if (!prefixcmp(line, "\\ ")) {
+ } else if (starts_with(line, "\\ ")) {
/*
* Eat the "no newline at eof" marker as if we
* saw a "+" or "-" line with nothing on it,
{
struct diffstat_file *x;
x = xcalloc(sizeof (*x), 1);
- if (diffstat->nr == diffstat->alloc) {
- diffstat->alloc = alloc_nr(diffstat->alloc);
- diffstat->files = xrealloc(diffstat->files,
- diffstat->alloc * sizeof(x));
- }
+ ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
diffstat->files[diffstat->nr++] = x;
if (name_b) {
x->from_name = xstrdup(name_a);
* 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,
(insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
insertions);
}
if (deletions || insertions == 0) {
- /*
- * TRANSLATORS: "-" in (-) is a line removal marker;
- * do not translate it.
- */
strbuf_addf(&sb,
(deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
deletions);
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
if (!diffopts)
;
- else if (!prefixcmp(diffopts, "--unified="))
+ else if (starts_with(diffopts, "--unified="))
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
- else if (!prefixcmp(diffopts, "-u"))
+ else if (starts_with(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
if (o->word_diff)
init_diff_words_data(&ecbdata, o, one, two);
remove_tempfile_installed = 1;
}
- if (!one->sha1_valid ||
- reuse_worktree_file(name, one->sha1, 1)) {
+ if (!S_ISGITLINK(one->mode) &&
+ (!one->sha1_valid ||
+ reuse_worktree_file(name, one->sha1, 1))) {
struct stat st;
if (lstat(name, &st) < 0) {
if (errno == ENOENT)
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
- int complete_rewrite)
+ int complete_rewrite,
+ struct diff_options *o)
{
- const char *spawn_arg[10];
+ struct argv_array argv = ARGV_ARRAY_INIT;
int retval;
- const char **arg = &spawn_arg[0];
+ struct diff_queue_struct *q = &diff_queued_diff;
+ const char *env[3] = { NULL };
+ char env_counter[50];
+ char env_total[50];
if (one && two) {
struct diff_tempfile *temp_one, *temp_two;
const char *othername = (other ? other : name);
temp_one = prepare_temp_file(name, one);
temp_two = prepare_temp_file(othername, two);
- *arg++ = pgm;
- *arg++ = name;
- *arg++ = temp_one->name;
- *arg++ = temp_one->hex;
- *arg++ = temp_one->mode;
- *arg++ = temp_two->name;
- *arg++ = temp_two->hex;
- *arg++ = temp_two->mode;
+ argv_array_push(&argv, pgm);
+ argv_array_push(&argv, name);
+ argv_array_push(&argv, temp_one->name);
+ argv_array_push(&argv, temp_one->hex);
+ argv_array_push(&argv, temp_one->mode);
+ argv_array_push(&argv, temp_two->name);
+ argv_array_push(&argv, temp_two->hex);
+ argv_array_push(&argv, temp_two->mode);
if (other) {
- *arg++ = other;
- *arg++ = xfrm_msg;
+ argv_array_push(&argv, other);
+ argv_array_push(&argv, xfrm_msg);
}
} else {
- *arg++ = pgm;
- *arg++ = name;
+ argv_array_push(&argv, pgm);
+ argv_array_push(&argv, name);
}
- *arg = NULL;
fflush(NULL);
- retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
+
+ env[0] = env_counter;
+ snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
+ ++o->diff_path_counter);
+ env[1] = env_total;
+ snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+
+ retval = run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env);
remove_tempfile();
+ argv_array_clear(&argv);
if (retval) {
fprintf(stderr, "external diff died, stopping at %s.\n", name);
exit(1);
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
- complete_rewrite);
+ complete_rewrite, o);
return;
}
if (one && two)
options->detect_rename = diff_detect_rename_default;
options->xdl_opts |= diff_algorithm;
+ options->orderfile = diff_order_file_cfg;
+
if (diff_no_prefix) {
options->a_prefix = options->b_prefix = "";
} else if (!diff_mnemonic_prefix) {
options->output_format = DIFF_FORMAT_NO_OUTPUT;
DIFF_OPT_SET(options, EXIT_WITH_STATUS);
}
+
+ options->diff_path_counter = 0;
}
static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
if (c != '-')
return 0;
arg++;
- eq = strchr(arg, '=');
- if (eq)
- len = eq - arg;
- else
- len = strlen(arg);
+ eq = strchrnul(arg, '=');
+ len = eq - arg;
if (!len || strncmp(arg, arg_long, len))
return 0;
- if (eq) {
+ if (*eq) {
int n;
char *end;
if (!isdigit(*++eq))
if (arg[0] != '-' || arg[1] != '-')
return 0;
arg += strlen("--");
- if (prefixcmp(arg, opt))
+ if (!starts_with(arg, opt))
return 0;
arg += strlen(opt);
- if (*arg == '=') { /* sticked form: --option=value */
+ if (*arg == '=') { /* stuck form: --option=value */
*optarg = arg + 1;
return 1;
}
switch (*arg) {
case '-':
- if (!prefixcmp(arg, "-width")) {
+ if (starts_with(arg, "-width")) {
arg += strlen("-width");
if (*arg == '=')
width = strtoul(arg + 1, &end, 10);
width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (!prefixcmp(arg, "-name-width")) {
+ } else if (starts_with(arg, "-name-width")) {
arg += strlen("-name-width");
if (*arg == '=')
name_width = strtoul(arg + 1, &end, 10);
name_width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (!prefixcmp(arg, "-graph-width")) {
+ } else if (starts_with(arg, "-graph-width")) {
arg += strlen("-graph-width");
if (*arg == '=')
graph_width = strtoul(arg + 1, &end, 10);
graph_width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (!prefixcmp(arg, "-count")) {
+ } else if (starts_with(arg, "-count")) {
arg += strlen("-count");
if (*arg == '=')
count = strtoul(arg + 1, &end, 10);
return 0;
}
-/* Used only by "diff-files" and "diff --no-index" */
-void handle_deprecated_show_diff_q(struct diff_options *opt)
-{
- warning("'diff -q' and 'diff-files -q' are deprecated.");
- warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs.");
- parse_diff_filter_opt("d", opt);
-}
-
static void enable_patch_output(int *fmt) {
*fmt &= ~DIFF_FORMAT_NO_OUTPUT;
*fmt |= DIFF_FORMAT_PATCH;
options->output_format |= DIFF_FORMAT_SHORTSTAT;
else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
return parse_dirstat_opt(options, "");
- else if (!prefixcmp(arg, "-X"))
+ else if (starts_with(arg, "-X"))
return parse_dirstat_opt(options, arg + 2);
- else if (!prefixcmp(arg, "--dirstat="))
+ else if (starts_with(arg, "--dirstat="))
return parse_dirstat_opt(options, arg + 10);
else if (!strcmp(arg, "--cumulative"))
return parse_dirstat_opt(options, "cumulative");
else if (!strcmp(arg, "--dirstat-by-file"))
return parse_dirstat_opt(options, "files");
- else if (!prefixcmp(arg, "--dirstat-by-file=")) {
+ else if (starts_with(arg, "--dirstat-by-file=")) {
parse_dirstat_opt(options, "files");
return parse_dirstat_opt(options, arg + 18);
}
options->output_format |= DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
options->output_format |= DIFF_FORMAT_NO_OUTPUT;
- else if (!prefixcmp(arg, "--stat"))
+ else if (starts_with(arg, "--stat"))
/* --stat, --stat-width, --stat-name-width, or --stat-count */
return stat_opt(options, av);
/* renames options */
- else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+ else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") ||
!strcmp(arg, "--break-rewrites")) {
if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
return error("invalid argument to -B: %s", arg+2);
}
- else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+ else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") ||
!strcmp(arg, "--find-renames")) {
if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
return error("invalid argument to -M: %s", arg+2);
else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
options->irreversible_delete = 1;
}
- else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+ else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
!strcmp(arg, "--find-copies")) {
if (options->detect_rename == DIFF_DETECT_COPY)
DIFF_OPT_SET(options, FIND_COPIES_HARDER);
DIFF_OPT_CLR(options, RENAME_EMPTY);
else if (!strcmp(arg, "--relative"))
DIFF_OPT_SET(options, RELATIVE_NAME);
- else if (!prefixcmp(arg, "--relative=")) {
+ else if (starts_with(arg, "--relative=")) {
DIFF_OPT_SET(options, RELATIVE_NAME);
options->prefix = arg + 11;
}
DIFF_OPT_CLR(options, FOLLOW_RENAMES);
else if (!strcmp(arg, "--color"))
options->use_color = 1;
- else if (!prefixcmp(arg, "--color=")) {
+ else if (starts_with(arg, "--color=")) {
int value = git_config_colorbool(NULL, arg+8);
if (value < 0)
return error("option `color' expects \"always\", \"auto\", or \"never\"");
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
}
- else if (!prefixcmp(arg, "--color-words=")) {
+ else if (starts_with(arg, "--color-words=")) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
options->word_regex = arg + 14;
if (options->word_diff == DIFF_WORDS_NONE)
options->word_diff = DIFF_WORDS_PLAIN;
}
- else if (!prefixcmp(arg, "--word-diff=")) {
+ else if (starts_with(arg, "--word-diff=")) {
const char *type = arg + 12;
if (!strcmp(type, "plain"))
options->word_diff = DIFF_WORDS_PLAIN;
else if (!strcmp(arg, "--ignore-submodules")) {
DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, "all");
- } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+ } else if (starts_with(arg, "--ignore-submodules=")) {
DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, arg + 20);
} else if (!strcmp(arg, "--submodule"))
DIFF_OPT_SET(options, SUBMODULE_LOG);
- else if (!prefixcmp(arg, "--submodule="))
+ else if (starts_with(arg, "--submodule="))
return parse_submodule_opt(options, arg + 12);
/* misc options */
}
else if (!strcmp(arg, "--abbrev"))
options->abbrev = DEFAULT_ABBREV;
- else if (!prefixcmp(arg, "--abbrev=")) {
+ else if (starts_with(arg, "--abbrev=")) {
options->abbrev = strtoul(arg + 9, NULL, 10);
if (options->abbrev < MINIMUM_ABBREV)
options->abbrev = MINIMUM_ABBREV;
cmd = *opt++;
if (cmd == '-') {
/* convert the long-form arguments into short-form versions */
- if (!prefixcmp(opt, "break-rewrites")) {
+ if (starts_with(opt, "break-rewrites")) {
opt += strlen("break-rewrites");
if (*opt == 0 || *opt++ == '=')
cmd = 'B';
- } else if (!prefixcmp(opt, "find-copies")) {
+ } else if (starts_with(opt, "find-copies")) {
opt += strlen("find-copies");
if (*opt == 0 || *opt++ == '=')
cmd = 'C';
- } else if (!prefixcmp(opt, "find-renames")) {
+ } else if (starts_with(opt, "find-renames")) {
opt += strlen("find-renames");
if (*opt == 0 || *opt++ == '=')
cmd = 'M';
}
}
if (cmd != 'M' && cmd != 'C' && cmd != 'B')
- return -1; /* that is not a -M, -C nor -B option */
+ return -1; /* that is not a -M, -C, or -B option */
opt1 = parse_rename_score(&opt);
if (cmd != 'B')
void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
{
- if (queue->alloc <= queue->nr) {
- queue->alloc = alloc_nr(queue->alloc);
- queue->queue = xrealloc(queue->queue,
- sizeof(dp) * queue->alloc);
- }
+ ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc);
queue->queue[queue->nr++] = dp;
}
DIFF_FILE_VALID(s) ? "valid" : "invalid",
s->mode,
s->sha1_valid ? sha1_to_hex(s->sha1) : "");
- fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
+ fprintf(stderr, "queue[%d] %s size %lu\n",
x, one ? one : "",
- s->size, s->xfrm_flags);
+ s->size);
}
void diff_debug_filepair(const struct diff_filepair *p, int i)
int new_len;
/* Ignore line numbers when computing the SHA1 of the patch */
- if (!prefixcmp(line, "@@ -"))
+ if (starts_with(line, "@@ -"))
return;
new_len = remove_space(line, len);
return !memcmp(one->data, two->data, one->size);
}
+static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+{
+ if (p->done_skip_stat_unmatch)
+ return p->skip_stat_unmatch_result;
+
+ p->done_skip_stat_unmatch = 1;
+ p->skip_stat_unmatch_result = 0;
+ /*
+ * 1. Entries that come from stat info dirtiness
+ * always have both sides (iow, not create/delete),
+ * one side of the object name is unknown, with
+ * the same mode and size. Keep the ones that
+ * do not match these criteria. They have real
+ * differences.
+ *
+ * 2. At this point, the file is known to be modified,
+ * with the same mode and size, and the object
+ * name of one side is unknown. Need to inspect
+ * the identical contents.
+ */
+ if (!DIFF_FILE_VALID(p->one) || /* (1) */
+ !DIFF_FILE_VALID(p->two) ||
+ (p->one->sha1_valid && p->two->sha1_valid) ||
+ (p->one->mode != p->two->mode) ||
+ diff_populate_filespec(p->one, 1) ||
+ diff_populate_filespec(p->two, 1) ||
+ (p->one->size != p->two->size) ||
+ !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ p->skip_stat_unmatch_result = 1;
+ return p->skip_stat_unmatch_result;
+}
+
static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
{
int i;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- /*
- * 1. Entries that come from stat info dirtiness
- * always have both sides (iow, not create/delete),
- * one side of the object name is unknown, with
- * the same mode and size. Keep the ones that
- * do not match these criteria. They have real
- * differences.
- *
- * 2. At this point, the file is known to be modified,
- * with the same mode and size, and the object
- * name of one side is unknown. Need to inspect
- * the identical contents.
- */
- if (!DIFF_FILE_VALID(p->one) || /* (1) */
- !DIFF_FILE_VALID(p->two) ||
- (p->one->sha1_valid && p->two->sha1_valid) ||
- (p->one->mode != p->two->mode) ||
- diff_populate_filespec(p->one, 1) ||
- diff_populate_filespec(p->two, 1) ||
- (p->one->size != p->two->size) ||
- !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ if (diff_filespec_check_stat_unmatch(p))
diff_q(&outq, p);
else {
/*
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
struct diff_filespec *one, *two;
+ struct diff_filepair *p;
if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
is_submodule_ignored(concatpath, options))
fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
one->dirty_submodule = old_dirty_submodule;
two->dirty_submodule = new_dirty_submodule;
+ p = diff_queue(&diff_queued_diff, one, two);
- diff_queue(&diff_queued_diff, one, two);
- if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+ return;
+
+ if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+ !diff_filespec_check_stat_unmatch(p))
+ return;
+
+ DIFF_OPT_SET(options, HAS_CHANGES);
}
struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)