diff options
Diffstat (limited to 'diff.c')
| -rw-r--r-- | diff.c | 336 | 
1 files changed, 171 insertions, 165 deletions
| @@ -16,6 +16,7 @@  #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 @@ -30,6 +31,7 @@ static int diff_use_color_default = -1;  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; @@ -50,23 +52,23 @@ static char diff_colors[][COLOR_MAXLEN] = {  	GIT_COLOR_NORMAL,	/* FUNCINFO */  }; -static int parse_diff_color_slot(const char *var, int ofs) +static int parse_diff_color_slot(const char *var)  { -	if (!strcasecmp(var+ofs, "plain")) +	if (!strcasecmp(var, "plain"))  		return DIFF_PLAIN; -	if (!strcasecmp(var+ofs, "meta")) +	if (!strcasecmp(var, "meta"))  		return DIFF_METAINFO; -	if (!strcasecmp(var+ofs, "frag")) +	if (!strcasecmp(var, "frag"))  		return DIFF_FRAGINFO; -	if (!strcasecmp(var+ofs, "old")) +	if (!strcasecmp(var, "old"))  		return DIFF_FILE_OLD; -	if (!strcasecmp(var+ofs, "new")) +	if (!strcasecmp(var, "new"))  		return DIFF_FILE_NEW; -	if (!strcasecmp(var+ofs, "commit")) +	if (!strcasecmp(var, "commit"))  		return DIFF_COMMIT; -	if (!strcasecmp(var+ofs, "whitespace")) +	if (!strcasecmp(var, "whitespace"))  		return DIFF_WHITESPACE; -	if (!strcasecmp(var+ofs, "func")) +	if (!strcasecmp(var, "func"))  		return DIFF_FUNCINFO;  	return -1;  } @@ -201,6 +203,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)  		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); @@ -227,6 +231,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)  int git_diff_basic_config(const char *var, const char *value, void *cb)  { +	const char *name; +  	if (!strcmp(var, "diff.renamelimit")) {  		diff_rename_limit_default = git_config_int(var, value);  		return 0; @@ -235,8 +241,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)  	if (userdiff_config(var, value) < 0)  		return -1; -	if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { -		int slot = parse_diff_color_slot(var, 11); +	if (skip_prefix(var, "diff.color.", &name) || +	    skip_prefix(var, "color.diff.", &name)) { +		int slot = parse_diff_color_slot(name);  		if (slot < 0)  			return 0;  		if (!value) @@ -264,7 +271,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)  		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); @@ -518,9 +525,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,  	ep += 2; /* skip over @@ */  	/* The hunk header in fraginfo color */ -	strbuf_add(&msgbuf, frag, strlen(frag)); +	strbuf_addstr(&msgbuf, frag);  	strbuf_add(&msgbuf, line, ep - line); -	strbuf_add(&msgbuf, reset, strlen(reset)); +	strbuf_addstr(&msgbuf, reset);  	/*  	 * trailing "\r\n" @@ -534,15 +541,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata,  		if (*ep != ' ' && *ep != '\t')  			break;  	if (ep != cp) { -		strbuf_add(&msgbuf, plain, strlen(plain)); +		strbuf_addstr(&msgbuf, plain);  		strbuf_add(&msgbuf, cp, ep - cp); -		strbuf_add(&msgbuf, reset, strlen(reset)); +		strbuf_addstr(&msgbuf, reset);  	}  	if (ep < line + len) { -		strbuf_add(&msgbuf, func, strlen(func)); +		strbuf_addstr(&msgbuf, func);  		strbuf_add(&msgbuf, ep, line + len - ep); -		strbuf_add(&msgbuf, reset, strlen(reset)); +		strbuf_addstr(&msgbuf, reset);  	}  	strbuf_add(&msgbuf, line + len, org_len - len); @@ -1215,7 +1222,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)  			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, @@ -1357,12 +1364,8 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,  					  const char *name_b)  {  	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)); -	} +	x = xcalloc(1, 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); @@ -1462,20 +1465,12 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)  	 * 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); @@ -2349,6 +2344,7 @@ static void builtin_diff(const char *name_a,  	} else {  		/* Crazy xdl interfaces.. */  		const char *diffopts = getenv("GIT_DIFF_OPTS"); +		const char *v;  		xpparam_t xpp;  		xdemitconf_t xecfg;  		struct emit_callback ecbdata; @@ -2387,10 +2383,10 @@ static void builtin_diff(const char *name_a,  			xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);  		if (!diffopts)  			; -		else if (!prefixcmp(diffopts, "--unified=")) -			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); -		else if (!prefixcmp(diffopts, "-u")) -			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); +		else if (skip_prefix(diffopts, "--unified=", &v)) +			xecfg.ctxlen = strtoul(v, NULL, 10); +		else if (skip_prefix(diffopts, "-u", &v)) +			xecfg.ctxlen = strtoul(v, NULL, 10);  		if (o->word_diff)  			init_diff_words_data(&ecbdata, o, one, two);  		xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, @@ -2842,8 +2838,9 @@ static struct diff_tempfile *prepare_temp_file(const char *name,  		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) @@ -2887,6 +2884,16 @@ static struct diff_tempfile *prepare_temp_file(const char *name,  	return temp;  } +static void add_external_diff_name(struct argv_array *argv, +				   const char *name, +				   struct diff_filespec *df) +{ +	struct diff_tempfile *temp = prepare_temp_file(name, df); +	argv_array_push(argv, temp->name); +	argv_array_push(argv, temp->hex); +	argv_array_push(argv, temp->mode); +} +  /* An external diff command takes:   *   * diff-cmd name infile1 infile1-sha1 infile1-mode \ @@ -2899,41 +2906,36 @@ static void run_external_diff(const char *pgm,  			      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]; -	int retval; -	const char **arg = &spawn_arg[0]; +	struct argv_array argv = ARGV_ARRAY_INIT; +	struct argv_array env = ARGV_ARRAY_INIT; +	struct diff_queue_struct *q = &diff_queued_diff; + +	argv_array_push(&argv, pgm); +	argv_array_push(&argv, name);  	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; -		if (other) { -			*arg++ = other; -			*arg++ = xfrm_msg; +		add_external_diff_name(&argv, name, one); +		if (!other) +			add_external_diff_name(&argv, name, two); +		else { +			add_external_diff_name(&argv, other, two); +			argv_array_push(&argv, other); +			argv_array_push(&argv, xfrm_msg);  		} -	} else { -		*arg++ = pgm; -		*arg++ = name;  	} -	*arg = NULL; -	fflush(NULL); -	retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL); + +	argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); +	argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); + +	if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv)) +		die(_("external diff died, stopping at %s"), name); +  	remove_tempfile(); -	if (retval) { -		fprintf(stderr, "external diff died, stopping at %s.\n", name); -		exit(1); -	} +	argv_array_clear(&argv); +	argv_array_clear(&env);  }  static int similarity_index(struct diff_filepair *p) @@ -3042,7 +3044,7 @@ static void run_diff_cmd(const char *pgm,  	if (pgm) {  		run_external_diff(pgm, name, other, one, two, xfrm_msg, -				  complete_rewrite); +				  complete_rewrite, o);  		return;  	}  	if (one && two) @@ -3201,12 +3203,15 @@ void diff_setup(struct diff_options *options)  	options->context = diff_context_default;  	DIFF_OPT_SET(options, RENAME_EMPTY); +	/* pathchange left =NULL by default */  	options->change = diff_change;  	options->add_remove = diff_addremove;  	options->use_color = diff_use_color_default;  	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) { @@ -3317,6 +3322,11 @@ void diff_setup_done(struct diff_options *options)  		options->output_format = DIFF_FORMAT_NO_OUTPUT;  		DIFF_OPT_SET(options, EXIT_WITH_STATUS);  	} + +	options->diff_path_counter = 0; + +	if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1) +		die(_("--follow requires exactly one pathspec"));  }  static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@ -3346,14 +3356,11 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va  	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)) @@ -3388,13 +3395,11 @@ int parse_long_opt(const char *opt, const char **argv,  		   const char **optarg)  {  	const char *arg = argv[0]; -	if (arg[0] != '-' || arg[1] != '-') +	if (!skip_prefix(arg, "--", &arg))  		return 0; -	arg += strlen("--"); -	if (prefixcmp(arg, opt)) +	if (!skip_prefix(arg, opt, &arg))  		return 0; -	arg += strlen(opt); -	if (*arg == '=') { /* sticked form: --option=value */ +	if (*arg == '=') { /* stuck form: --option=value */  		*optarg = arg + 1;  		return 1;  	} @@ -3417,13 +3422,13 @@ static int stat_opt(struct diff_options *options, const char **av)  	int count = options->stat_count;  	int argcount = 1; -	arg += strlen("--stat"); +	if (!skip_prefix(arg, "--stat", &arg)) +		die("BUG: stat option does not begin with --stat: %s", arg);  	end = (char *)arg;  	switch (*arg) {  	case '-': -		if (!prefixcmp(arg, "-width")) { -			arg += strlen("-width"); +		if (skip_prefix(arg, "-width", &arg)) {  			if (*arg == '=')  				width = strtoul(arg + 1, &end, 10);  			else if (!*arg && !av[1]) @@ -3432,8 +3437,7 @@ static int stat_opt(struct diff_options *options, const char **av)  				width = strtoul(av[1], &end, 10);  				argcount = 2;  			} -		} else if (!prefixcmp(arg, "-name-width")) { -			arg += strlen("-name-width"); +		} else if (skip_prefix(arg, "-name-width", &arg)) {  			if (*arg == '=')  				name_width = strtoul(arg + 1, &end, 10);  			else if (!*arg && !av[1]) @@ -3442,8 +3446,7 @@ static int stat_opt(struct diff_options *options, const char **av)  				name_width = strtoul(av[1], &end, 10);  				argcount = 2;  			} -		} else if (!prefixcmp(arg, "-graph-width")) { -			arg += strlen("-graph-width"); +		} else if (skip_prefix(arg, "-graph-width", &arg)) {  			if (*arg == '=')  				graph_width = strtoul(arg + 1, &end, 10);  			else if (!*arg && !av[1]) @@ -3452,8 +3455,7 @@ static int stat_opt(struct diff_options *options, const char **av)  				graph_width = strtoul(av[1], &end, 10);  				argcount = 2;  			} -		} else if (!prefixcmp(arg, "-count")) { -			arg += strlen("-count"); +		} else if (skip_prefix(arg, "-count", &arg)) {  			if (*arg == '=')  				count = strtoul(arg + 1, &end, 10);  			else if (!*arg && !av[1]) @@ -3580,14 +3582,6 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)  	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; @@ -3614,17 +3608,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  		options->output_format |= DIFF_FORMAT_SHORTSTAT;  	else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))  		return parse_dirstat_opt(options, ""); -	else if (!prefixcmp(arg, "-X")) -		return parse_dirstat_opt(options, arg + 2); -	else if (!prefixcmp(arg, "--dirstat=")) -		return parse_dirstat_opt(options, arg + 10); +	else if (skip_prefix(arg, "-X", &arg)) +		return parse_dirstat_opt(options, arg); +	else if (skip_prefix(arg, "--dirstat=", &arg)) +		return parse_dirstat_opt(options, arg);  	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 (skip_prefix(arg, "--dirstat-by-file=", &arg)) {  		parse_dirstat_opt(options, "files"); -		return parse_dirstat_opt(options, arg + 18); +		return parse_dirstat_opt(options, arg);  	}  	else if (!strcmp(arg, "--check"))  		options->output_format |= DIFF_FORMAT_CHECKDIFF; @@ -3639,17 +3633,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  		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); @@ -3658,7 +3652,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  	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); @@ -3674,9 +3668,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  		DIFF_OPT_CLR(options, RENAME_EMPTY);  	else if (!strcmp(arg, "--relative"))  		DIFF_OPT_SET(options, RELATIVE_NAME); -	else if (!prefixcmp(arg, "--relative=")) { +	else if (skip_prefix(arg, "--relative=", &arg)) {  		DIFF_OPT_SET(options, RELATIVE_NAME); -		options->prefix = arg + 11; +		options->prefix = arg;  	}  	/* xdiff options */ @@ -3727,8 +3721,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  		DIFF_OPT_CLR(options, FOLLOW_RENAMES);  	else if (!strcmp(arg, "--color"))  		options->use_color = 1; -	else if (!prefixcmp(arg, "--color=")) { -		int value = git_config_colorbool(NULL, arg+8); +	else if (skip_prefix(arg, "--color=", &arg)) { +		int value = git_config_colorbool(NULL, arg);  		if (value < 0)  			return error("option `color' expects \"always\", \"auto\", or \"never\"");  		options->use_color = value; @@ -3739,29 +3733,28 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  		options->use_color = 1;  		options->word_diff = DIFF_WORDS_COLOR;  	} -	else if (!prefixcmp(arg, "--color-words=")) { +	else if (skip_prefix(arg, "--color-words=", &arg)) {  		options->use_color = 1;  		options->word_diff = DIFF_WORDS_COLOR; -		options->word_regex = arg + 14; +		options->word_regex = arg;  	}  	else if (!strcmp(arg, "--word-diff")) {  		if (options->word_diff == DIFF_WORDS_NONE)  			options->word_diff = DIFF_WORDS_PLAIN;  	} -	else if (!prefixcmp(arg, "--word-diff=")) { -		const char *type = arg + 12; -		if (!strcmp(type, "plain")) +	else if (skip_prefix(arg, "--word-diff=", &arg)) { +		if (!strcmp(arg, "plain"))  			options->word_diff = DIFF_WORDS_PLAIN; -		else if (!strcmp(type, "color")) { +		else if (!strcmp(arg, "color")) {  			options->use_color = 1;  			options->word_diff = DIFF_WORDS_COLOR;  		} -		else if (!strcmp(type, "porcelain")) +		else if (!strcmp(arg, "porcelain"))  			options->word_diff = DIFF_WORDS_PORCELAIN; -		else if (!strcmp(type, "none")) +		else if (!strcmp(arg, "none"))  			options->word_diff = DIFF_WORDS_NONE;  		else -			die("bad --word-diff argument: %s", type); +			die("bad --word-diff argument: %s", arg);  	}  	else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {  		if (options->word_diff == DIFF_WORDS_NONE) @@ -3784,13 +3777,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  	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 (skip_prefix(arg, "--ignore-submodules=", &arg)) {  		DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); -		handle_ignore_submodules_arg(options, arg + 20); +		handle_ignore_submodules_arg(options, arg);  	} else if (!strcmp(arg, "--submodule"))  		DIFF_OPT_SET(options, SUBMODULE_LOG); -	else if (!prefixcmp(arg, "--submodule=")) -		return parse_submodule_opt(options, arg + 12); +	else if (skip_prefix(arg, "--submodule=", &arg)) +		return parse_submodule_opt(options, arg);  	/* misc options */  	else if (!strcmp(arg, "-z")) @@ -3825,8 +3818,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)  	}  	else if (!strcmp(arg, "--abbrev"))  		options->abbrev = DEFAULT_ABBREV; -	else if (!prefixcmp(arg, "--abbrev=")) { -		options->abbrev = strtoul(arg + 9, NULL, 10); +	else if (skip_prefix(arg, "--abbrev=", &arg)) { +		options->abbrev = strtoul(arg, NULL, 10);  		if (options->abbrev < MINIMUM_ABBREV)  			options->abbrev = MINIMUM_ABBREV;  		else if (40 < options->abbrev) @@ -3907,22 +3900,19 @@ static int diff_scoreopt_parse(const char *opt)  	cmd = *opt++;  	if (cmd == '-') {  		/* convert the long-form arguments into short-form versions */ -		if (!prefixcmp(opt, "break-rewrites")) { -			opt += strlen("break-rewrites"); +		if (skip_prefix(opt, "break-rewrites", &opt)) {  			if (*opt == 0 || *opt++ == '=')  				cmd = 'B'; -		} else if (!prefixcmp(opt, "find-copies")) { -			opt += strlen("find-copies"); +		} else if (skip_prefix(opt, "find-copies", &opt)) {  			if (*opt == 0 || *opt++ == '=')  				cmd = 'C'; -		} else if (!prefixcmp(opt, "find-renames")) { -			opt += strlen("find-renames"); +		} else if (skip_prefix(opt, "find-renames", &opt)) {  			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') @@ -3946,11 +3936,7 @@ struct diff_queue_struct diff_queued_diff;  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;  } @@ -4120,9 +4106,9 @@ void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)  		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) @@ -4325,7 +4311,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)  	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); @@ -4678,6 +4664,38 @@ static int diff_filespec_is_identical(struct diff_filespec *one,  	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; @@ -4688,27 +4706,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)  	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 {  			/* @@ -4744,6 +4742,7 @@ void diffcore_fix_diff_index(struct diff_options *options)  void diffcore_std(struct diff_options *options)  { +	/* NOTE please keep the following in sync with diff_tree_combined() */  	if (options->skip_stat_unmatch)  		diffcore_skip_stat_unmatch(options);  	if (!options->found_follow) { @@ -4871,6 +4870,7 @@ void diff_change(struct diff_options *options,  		 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)) @@ -4897,10 +4897,16 @@ void diff_change(struct diff_options *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) | 
