diff.c 173.3 KB
Newer Older
J
Junio C Hamano 已提交
1 2 3 4
/*
 * Copyright (C) 2005 Junio C Hamano
 */
#include "cache.h"
5
#include "config.h"
M
Michael Haggerty 已提交
6
#include "tempfile.h"
J
Junio C Hamano 已提交
7 8 9
#include "quote.h"
#include "diff.h"
#include "diffcore.h"
J
Junio C Hamano 已提交
10
#include "delta.h"
J
Junio C Hamano 已提交
11
#include "xdiff-interface.h"
12
#include "color.h"
13
#include "attr.h"
14
#include "run-command.h"
15
#include "utf8.h"
16
#include "object-store.h"
17
#include "userdiff.h"
18
#include "submodule-config.h"
19
#include "submodule.h"
20
#include "hashmap.h"
21
#include "ll-merge.h"
22
#include "string-list.h"
23
#include "argv-array.h"
24
#include "graph.h"
J
Jonathan Tan 已提交
25
#include "packfile.h"
26
#include "help.h"
J
Junio C Hamano 已提交
27

28 29 30 31 32 33
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
#else
#define FAST_WORKING_DIRECTORY 1
#endif

34
static int diff_detect_rename_default;
35
static int diff_indent_heuristic = 1;
J
Jeff King 已提交
36
static int diff_rename_limit_default = 400;
37
static int diff_suppress_blank_empty;
38
static int diff_use_color_default = -1;
39
static int diff_color_moved_default;
40
static int diff_color_moved_ws_default;
41
static int diff_context_default = 3;
42
static int diff_interhunk_context_default;
43
static const char *diff_word_regex_cfg;
44
static const char *external_diff_cmd_cfg;
45
static const char *diff_order_file_cfg;
46
int diff_auto_refresh_index = 1;
47
static int diff_mnemonic_prefix;
48
static int diff_no_prefix;
49
static int diff_stat_graph_width;
50
static int diff_dirstat_permille_default = 30;
51
static struct diff_options default_diff_options;
52
static long diff_algorithm;
53
static unsigned ws_error_highlight_default = WSEH_NEW;
J
Junio C Hamano 已提交
54

55
static char diff_colors[][COLOR_MAXLEN] = {
56
	GIT_COLOR_RESET,
57
	GIT_COLOR_NORMAL,	/* CONTEXT */
58 59 60 61 62 63
	GIT_COLOR_BOLD,		/* METAINFO */
	GIT_COLOR_CYAN,		/* FRAGINFO */
	GIT_COLOR_RED,		/* OLD */
	GIT_COLOR_GREEN,	/* NEW */
	GIT_COLOR_YELLOW,	/* COMMIT */
	GIT_COLOR_BG_RED,	/* WHITESPACE */
B
Bert Wesarg 已提交
64
	GIT_COLOR_NORMAL,	/* FUNCINFO */
65 66 67 68 69 70 71 72
	GIT_COLOR_BOLD_MAGENTA,	/* OLD_MOVED */
	GIT_COLOR_BOLD_BLUE,	/* OLD_MOVED ALTERNATIVE */
	GIT_COLOR_FAINT,	/* OLD_MOVED_DIM */
	GIT_COLOR_FAINT_ITALIC,	/* OLD_MOVED_ALTERNATIVE_DIM */
	GIT_COLOR_BOLD_CYAN,	/* NEW_MOVED */
	GIT_COLOR_BOLD_YELLOW,	/* NEW_MOVED ALTERNATIVE */
	GIT_COLOR_FAINT,	/* NEW_MOVED_DIM */
	GIT_COLOR_FAINT_ITALIC,	/* NEW_MOVED_ALTERNATIVE_DIM */
73 74 75 76 77 78
	GIT_COLOR_FAINT,	/* CONTEXT_DIM */
	GIT_COLOR_FAINT_RED,	/* OLD_DIM */
	GIT_COLOR_FAINT_GREEN,	/* NEW_DIM */
	GIT_COLOR_BOLD,		/* CONTEXT_BOLD */
	GIT_COLOR_BOLD_RED,	/* OLD_BOLD */
	GIT_COLOR_BOLD_GREEN,	/* NEW_BOLD */
J
Johannes Schindelin 已提交
79 80
};

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
static const char *color_diff_slots[] = {
	[DIFF_CONTEXT]		      = "context",
	[DIFF_METAINFO]		      = "meta",
	[DIFF_FRAGINFO]		      = "frag",
	[DIFF_FILE_OLD]		      = "old",
	[DIFF_FILE_NEW]		      = "new",
	[DIFF_COMMIT]		      = "commit",
	[DIFF_WHITESPACE]	      = "whitespace",
	[DIFF_FUNCINFO]		      = "func",
	[DIFF_FILE_OLD_MOVED]	      = "oldMoved",
	[DIFF_FILE_OLD_MOVED_ALT]     = "oldMovedAlternative",
	[DIFF_FILE_OLD_MOVED_DIM]     = "oldMovedDimmed",
	[DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed",
	[DIFF_FILE_NEW_MOVED]	      = "newMoved",
	[DIFF_FILE_NEW_MOVED_ALT]     = "newMovedAlternative",
	[DIFF_FILE_NEW_MOVED_DIM]     = "newMovedDimmed",
	[DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed",
98 99 100 101 102 103
	[DIFF_CONTEXT_DIM]	      = "contextDimmed",
	[DIFF_FILE_OLD_DIM]	      = "oldDimmed",
	[DIFF_FILE_NEW_DIM]	      = "newDimmed",
	[DIFF_CONTEXT_BOLD]	      = "contextBold",
	[DIFF_FILE_OLD_BOLD]	      = "oldBold",
	[DIFF_FILE_NEW_BOLD]	      = "newBold",
104 105
};

106 107 108 109 110
static NORETURN void die_want_option(const char *option_name)
{
	die(_("option '%s' requires a value"), option_name);
}

111 112
define_list_config_array_extra(color_diff_slots, {"plain"});

113
static int parse_diff_color_slot(const char *var)
114
{
115
	if (!strcasecmp(var, "plain"))
116
		return DIFF_CONTEXT;
117
	return LOOKUP_CONFIG(color_diff_slots, var);
118 119
}

120
static int parse_dirstat_params(struct diff_options *options, const char *params_string,
121
				struct strbuf *errmsg)
122
{
123 124 125 126
	char *params_copy = xstrdup(params_string);
	struct string_list params = STRING_LIST_INIT_NODUP;
	int ret = 0;
	int i;
127

128 129 130 131 132
	if (*params_copy)
		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")) {
133 134
			options->flags.dirstat_by_line = 0;
			options->flags.dirstat_by_file = 0;
135
		} else if (!strcmp(p, "lines")) {
136 137
			options->flags.dirstat_by_line = 1;
			options->flags.dirstat_by_file = 0;
138
		} else if (!strcmp(p, "files")) {
139 140
			options->flags.dirstat_by_line = 0;
			options->flags.dirstat_by_file = 1;
141
		} else if (!strcmp(p, "noncumulative")) {
142
			options->flags.dirstat_cumulative = 0;
143
		} else if (!strcmp(p, "cumulative")) {
144
			options->flags.dirstat_cumulative = 1;
145 146
		} else if (isdigit(*p)) {
			char *end;
147 148
			int permille = strtoul(p, &end, 10) * 10;
			if (*end == '.' && isdigit(*++end)) {
149
				/* only use first digit */
150
				permille += *end - '0';
151
				/* .. and ignore any further digits */
152
				while (isdigit(*++end))
153 154
					; /* nothing */
			}
155
			if (!*end)
156 157
				options->dirstat_permille = permille;
			else {
158 159
				strbuf_addf(errmsg, _("  Failed to parse dirstat cut-off percentage '%s'\n"),
					    p);
160 161 162
				ret++;
			}
		} else {
163
			strbuf_addf(errmsg, _("  Unknown dirstat parameter '%s'\n"), p);
164
			ret++;
165
		}
166

167
	}
168 169
	string_list_clear(&params, 0);
	free(params_copy);
170
	return ret;
171 172
}

173 174 175
static int parse_submodule_params(struct diff_options *options, const char *value)
{
	if (!strcmp(value, "log"))
176
		options->submodule_format = DIFF_SUBMODULE_LOG;
177
	else if (!strcmp(value, "short"))
178
		options->submodule_format = DIFF_SUBMODULE_SHORT;
179 180
	else if (!strcmp(value, "diff"))
		options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
181 182 183 184 185
	else
		return -1;
	return 0;
}

186
int git_config_rename(const char *var, const char *value)
187 188 189 190 191 192 193 194
{
	if (!value)
		return DIFF_DETECT_RENAME;
	if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
		return  DIFF_DETECT_COPY;
	return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
}

195
long parse_algorithm_value(const char *value)
196 197 198 199 200 201 202 203 204 205 206 207 208 209
{
	if (!value)
		return -1;
	else if (!strcasecmp(value, "myers") || !strcasecmp(value, "default"))
		return 0;
	else if (!strcasecmp(value, "minimal"))
		return XDF_NEED_MINIMAL;
	else if (!strcasecmp(value, "patience"))
		return XDF_PATIENCE_DIFF;
	else if (!strcasecmp(value, "histogram"))
		return XDF_HISTOGRAM_DIFF;
	return -1;
}

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
static int parse_one_token(const char **arg, const char *token)
{
	const char *rest;
	if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
		*arg = rest;
		return 1;
	}
	return 0;
}

static int parse_ws_error_highlight(const char *arg)
{
	const char *orig_arg = arg;
	unsigned val = 0;

	while (*arg) {
		if (parse_one_token(&arg, "none"))
			val = 0;
		else if (parse_one_token(&arg, "default"))
			val = WSEH_NEW;
		else if (parse_one_token(&arg, "all"))
			val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
		else if (parse_one_token(&arg, "new"))
			val |= WSEH_NEW;
		else if (parse_one_token(&arg, "old"))
			val |= WSEH_OLD;
		else if (parse_one_token(&arg, "context"))
			val |= WSEH_CONTEXT;
		else {
			return -1 - (int)(arg - orig_arg);
		}
		if (*arg)
			arg++;
	}
	return val;
}

247 248 249 250 251 252
/*
 * These are to give UI layer defaults.
 * The core-level commands such as git-diff-files should
 * never be affected by the setting of diff.renames
 * the user happens to have in the configuration file.
 */
253 254
void init_diff_ui_defaults(void)
{
255
	diff_detect_rename_default = DIFF_DETECT_RENAME;
256 257
}

258 259
int git_diff_heuristic_config(const char *var, const char *value, void *cb)
{
260
	if (!strcmp(var, "diff.indentheuristic"))
261 262 263 264
		diff_indent_heuristic = git_config_bool(var, value);
	return 0;
}

265 266 267 268 269 270 271 272 273 274 275 276 277
static int parse_color_moved(const char *arg)
{
	switch (git_parse_maybe_bool(arg)) {
	case 0:
		return COLOR_MOVED_NO;
	case 1:
		return COLOR_MOVED_DEFAULT;
	default:
		break;
	}

	if (!strcmp(arg, "no"))
		return COLOR_MOVED_NO;
278 279
	else if (!strcmp(arg, "plain"))
		return COLOR_MOVED_PLAIN;
280 281
	else if (!strcmp(arg, "blocks"))
		return COLOR_MOVED_BLOCKS;
282 283 284 285
	else if (!strcmp(arg, "zebra"))
		return COLOR_MOVED_ZEBRA;
	else if (!strcmp(arg, "default"))
		return COLOR_MOVED_DEFAULT;
286 287
	else if (!strcmp(arg, "dimmed-zebra"))
		return COLOR_MOVED_ZEBRA_DIM;
288 289
	else if (!strcmp(arg, "dimmed_zebra"))
		return COLOR_MOVED_ZEBRA_DIM;
290
	else
291
		return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
292 293
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
static int parse_color_moved_ws(const char *arg)
{
	int ret = 0;
	struct string_list l = STRING_LIST_INIT_DUP;
	struct string_list_item *i;

	string_list_split(&l, arg, ',', -1);

	for_each_string_list_item(i, &l) {
		struct strbuf sb = STRBUF_INIT;
		strbuf_addstr(&sb, i->string);
		strbuf_trim(&sb);

		if (!strcmp(sb.buf, "ignore-space-change"))
			ret |= XDF_IGNORE_WHITESPACE_CHANGE;
		else if (!strcmp(sb.buf, "ignore-space-at-eol"))
			ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
		else if (!strcmp(sb.buf, "ignore-all-space"))
			ret |= XDF_IGNORE_WHITESPACE;
313 314
		else if (!strcmp(sb.buf, "allow-indentation-change"))
			ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
315 316 317 318 319 320
		else
			error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);

		strbuf_release(&sb);
	}

321 322 323 324
	if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
	    (ret & XDF_WHITESPACE_FLAGS))
		die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));

325 326 327
	string_list_clear(&l, 0);

	return ret;
328 329
}

330
int git_diff_ui_config(const char *var, const char *value, void *cb)
331
{
332
	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
333
		diff_use_color_default = git_config_colorbool(var, value);
334 335
		return 0;
	}
336 337 338 339 340 341 342
	if (!strcmp(var, "diff.colormoved")) {
		int cm = parse_color_moved(value);
		if (cm < 0)
			return -1;
		diff_color_moved_default = cm;
		return 0;
	}
343 344 345 346 347 348 349
	if (!strcmp(var, "diff.colormovedws")) {
		int cm = parse_color_moved_ws(value);
		if (cm < 0)
			return -1;
		diff_color_moved_ws_default = cm;
		return 0;
	}
350 351 352 353 354 355
	if (!strcmp(var, "diff.context")) {
		diff_context_default = git_config_int(var, value);
		if (diff_context_default < 0)
			return -1;
		return 0;
	}
356 357 358 359 360 361
	if (!strcmp(var, "diff.interhunkcontext")) {
		diff_interhunk_context_default = git_config_int(var, value);
		if (diff_interhunk_context_default < 0)
			return -1;
		return 0;
	}
362
	if (!strcmp(var, "diff.renames")) {
363
		diff_detect_rename_default = git_config_rename(var, value);
364 365
		return 0;
	}
366 367 368 369
	if (!strcmp(var, "diff.autorefreshindex")) {
		diff_auto_refresh_index = git_config_bool(var, value);
		return 0;
	}
370 371 372 373
	if (!strcmp(var, "diff.mnemonicprefix")) {
		diff_mnemonic_prefix = git_config_bool(var, value);
		return 0;
	}
374 375 376 377
	if (!strcmp(var, "diff.noprefix")) {
		diff_no_prefix = git_config_bool(var, value);
		return 0;
	}
378 379 380 381
	if (!strcmp(var, "diff.statgraphwidth")) {
		diff_stat_graph_width = git_config_int(var, value);
		return 0;
	}
382 383
	if (!strcmp(var, "diff.external"))
		return git_config_string(&external_diff_cmd_cfg, var, value);
384 385
	if (!strcmp(var, "diff.wordregex"))
		return git_config_string(&diff_word_regex_cfg, var, value);
386 387
	if (!strcmp(var, "diff.orderfile"))
		return git_config_pathname(&diff_order_file_cfg, var, value);
J
Junio C Hamano 已提交
388

389 390 391
	if (!strcmp(var, "diff.ignoresubmodules"))
		handle_ignore_submodules_arg(&default_diff_options, value);

392 393 394 395 396 397 398
	if (!strcmp(var, "diff.submodule")) {
		if (parse_submodule_params(&default_diff_options, value))
			warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
				value);
		return 0;
	}

399 400 401 402 403 404 405
	if (!strcmp(var, "diff.algorithm")) {
		diff_algorithm = parse_algorithm_value(value);
		if (diff_algorithm < 0)
			return -1;
		return 0;
	}

406 407 408 409 410 411 412 413
	if (!strcmp(var, "diff.wserrorhighlight")) {
		int val = parse_ws_error_highlight(value);
		if (val < 0)
			return -1;
		ws_error_highlight_default = val;
		return 0;
	}

414 415 416
	if (git_color_config(var, value, cb) < 0)
		return -1;

417
	return git_diff_basic_config(var, value, cb);
J
Jeff King 已提交
418 419
}

420
int git_diff_basic_config(const char *var, const char *value, void *cb)
J
Jeff King 已提交
421
{
422 423
	const char *name;

424 425 426 427 428
	if (!strcmp(var, "diff.renamelimit")) {
		diff_rename_limit_default = git_config_int(var, value);
		return 0;
	}

429 430
	if (userdiff_config(var, value) < 0)
		return -1;
431

432 433 434
	if (skip_prefix(var, "diff.color.", &name) ||
	    skip_prefix(var, "color.diff.", &name)) {
		int slot = parse_diff_color_slot(name);
J
Jeff King 已提交
435 436
		if (slot < 0)
			return 0;
437 438
		if (!value)
			return config_error_nonbool(var);
439
		return color_parse(value, diff_colors[slot]);
440
	}
J
Junio C Hamano 已提交
441

442
	/* like GNU diff's --suppress-blank-empty option  */
443 444 445
	if (!strcmp(var, "diff.suppressblankempty") ||
			/* for backwards compatibility */
			!strcmp(var, "diff.suppress-blank-empty")) {
446 447 448 449
		diff_suppress_blank_empty = git_config_bool(var, value);
		return 0;
	}

450
	if (!strcmp(var, "diff.dirstat")) {
451
		struct strbuf errmsg = STRBUF_INIT;
452
		default_diff_options.dirstat_permille = diff_dirstat_permille_default;
453
		if (parse_dirstat_params(&default_diff_options, value, &errmsg))
454
			warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
455 456
				errmsg.buf);
		strbuf_release(&errmsg);
457
		diff_dirstat_permille_default = default_diff_options.dirstat_permille;
458 459 460
		return 0;
	}

461 462 463
	if (git_diff_heuristic_config(var, value, cb) < 0)
		return -1;

464
	return git_default_config(var, value, cb);
465 466
}

J
Junio C Hamano 已提交
467 468 469 470
static char *quote_two(const char *one, const char *two)
{
	int need_one = quote_c_style(one, NULL, NULL, 1);
	int need_two = quote_c_style(two, NULL, NULL, 1);
471
	struct strbuf res = STRBUF_INIT;
J
Junio C Hamano 已提交
472 473

	if (need_one + need_two) {
474 475 476 477 478 479 480
		strbuf_addch(&res, '"');
		quote_c_style(one, &res, NULL, 1);
		quote_c_style(two, &res, NULL, 1);
		strbuf_addch(&res, '"');
	} else {
		strbuf_addstr(&res, one);
		strbuf_addstr(&res, two);
J
Junio C Hamano 已提交
481
	}
482
	return strbuf_detach(&res, NULL);
J
Junio C Hamano 已提交
483 484 485 486 487 488 489 490 491 492
}

static const char *external_diff(void)
{
	static const char *external_diff_cmd = NULL;
	static int done_preparing = 0;

	if (done_preparing)
		return external_diff_cmd;
	external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
493 494
	if (!external_diff_cmd)
		external_diff_cmd = external_diff_cmd_cfg;
J
Junio C Hamano 已提交
495 496 497 498
	done_preparing = 1;
	return external_diff_cmd;
}

M
Michael Haggerty 已提交
499 500 501 502 503
/*
 * Keep track of files used for diffing. Sometimes such an entry
 * refers to a temporary file, sometimes to an existing file, and
 * sometimes to "/dev/null".
 */
J
Junio C Hamano 已提交
504
static struct diff_tempfile {
M
Michael Haggerty 已提交
505 506 507 508 509 510
	/*
	 * filename external diff should read from, or NULL if this
	 * entry is currently not in use:
	 */
	const char *name;

511
	char hex[GIT_MAX_HEXSZ + 1];
J
Junio C Hamano 已提交
512
	char mode[10];
M
Michael Haggerty 已提交
513 514 515 516 517

	/*
	 * If this diff_tempfile instance refers to a temporary file,
	 * this tempfile object is used to manage its lifetime.
	 */
518
	struct tempfile *tempfile;
J
Junio C Hamano 已提交
519 520
} diff_temp[2];

J
Junio C Hamano 已提交
521 522 523 524 525 526 527 528 529
struct emit_callback {
	int color_diff;
	unsigned ws_rule;
	int blank_at_eof_in_preimage;
	int blank_at_eof_in_postimage;
	int lno_in_preimage;
	int lno_in_postimage;
	const char **label_path;
	struct diff_words_data *diff_words;
530
	struct diff_options *opt;
531
	struct strbuf *header;
J
Junio C Hamano 已提交
532 533
};

J
Junio C Hamano 已提交
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
static int count_lines(const char *data, int size)
{
	int count, ch, completely_empty = 1, nl_just_seen = 0;
	count = 0;
	while (0 < size--) {
		ch = *data++;
		if (ch == '\n') {
			count++;
			nl_just_seen = 1;
			completely_empty = 0;
		}
		else {
			nl_just_seen = 0;
			completely_empty = 0;
		}
	}
	if (completely_empty)
		return 0;
	if (!nl_just_seen)
		count++; /* no trailing newline */
	return count;
}

J
Junio C Hamano 已提交
557 558 559 560 561 562 563 564 565
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{
	if (!DIFF_FILE_VALID(one)) {
		mf->ptr = (char *)""; /* does not matter */
		mf->size = 0;
		return 0;
	}
	else if (diff_populate_filespec(one, 0))
		return -1;
566

J
Junio C Hamano 已提交
567 568 569 570 571
	mf->ptr = one->data;
	mf->size = one->size;
	return 0;
}

572 573 574 575 576
/* like fill_mmfile, but only for size, so we can avoid retrieving blob */
static unsigned long diff_filespec_size(struct diff_filespec *one)
{
	if (!DIFF_FILE_VALID(one))
		return 0;
577
	diff_populate_filespec(one, CHECK_SIZE_ONLY);
578 579 580
	return one->size;
}

J
Junio C Hamano 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
{
	char *ptr = mf->ptr;
	long size = mf->size;
	int cnt = 0;

	if (!size)
		return cnt;
	ptr += size - 1; /* pointing at the very end */
	if (*ptr != '\n')
		; /* incomplete line */
	else
		ptr--; /* skip the last LF */
	while (mf->ptr < ptr) {
		char *prev_eol;
		for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
			if (*prev_eol == '\n')
				break;
		if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
			break;
		cnt++;
		ptr = prev_eol - 1;
	}
	return cnt;
}

static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
			       struct emit_callback *ecbdata)
{
	int l1, l2, at;
	unsigned ws_rule = ecbdata->ws_rule;
	l1 = count_trailing_blank(mf1, ws_rule);
	l2 = count_trailing_blank(mf2, ws_rule);
	if (l2 <= l1) {
		ecbdata->blank_at_eof_in_preimage = 0;
		ecbdata->blank_at_eof_in_postimage = 0;
		return;
	}
	at = count_lines(mf1->ptr, mf1->size);
	ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;

	at = count_lines(mf2->ptr, mf2->size);
	ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
}

626 627
static void emit_line_0(struct diff_options *o,
			const char *set, unsigned reverse, const char *reset,
628
			int first, const char *line, int len)
J
Junio C Hamano 已提交
629 630
{
	int has_trailing_newline, has_trailing_carriage_return;
631
	int nofirst;
632 633
	FILE *file = o->file;

634 635 636 637
	if (first)
		fputs(diff_line_prefix(o), file);
	else if (!len)
		return;
J
Junio C Hamano 已提交
638

639 640 641 642 643 644 645 646 647 648 649 650 651 652
	if (len == 0) {
		has_trailing_newline = (first == '\n');
		has_trailing_carriage_return = (!has_trailing_newline &&
						(first == '\r'));
		nofirst = has_trailing_newline || has_trailing_carriage_return;
	} else {
		has_trailing_newline = (len > 0 && line[len-1] == '\n');
		if (has_trailing_newline)
			len--;
		has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
		if (has_trailing_carriage_return)
			len--;
		nofirst = 0;
	}
J
Junio C Hamano 已提交
653

654
	if (len || !nofirst) {
655 656
		if (reverse && want_color(o->use_color))
			fputs(GIT_COLOR_REVERSE, file);
657
		fputs(set, file);
658
		if (first && !nofirst)
659 660 661 662
			fputc(first, file);
		fwrite(line, len, 1, file);
		fputs(reset, file);
	}
J
Junio C Hamano 已提交
663 664 665 666 667 668
	if (has_trailing_carriage_return)
		fputc('\r', file);
	if (has_trailing_newline)
		fputc('\n', file);
}

669
static void emit_line(struct diff_options *o, const char *set, const char *reset,
670 671
		      const char *line, int len)
{
672
	emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
673 674
}

675
enum diff_symbol {
676 677 678 679 680
	DIFF_SYMBOL_BINARY_DIFF_HEADER,
	DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
	DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
	DIFF_SYMBOL_BINARY_DIFF_BODY,
	DIFF_SYMBOL_BINARY_DIFF_FOOTER,
681 682 683 684
	DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
	DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
	DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
	DIFF_SYMBOL_STATS_LINE,
685
	DIFF_SYMBOL_WORD_DIFF,
686
	DIFF_SYMBOL_STAT_SEP,
687
	DIFF_SYMBOL_SUMMARY,
688 689 690 691 692 693 694
	DIFF_SYMBOL_SUBMODULE_ADD,
	DIFF_SYMBOL_SUBMODULE_DEL,
	DIFF_SYMBOL_SUBMODULE_UNTRACKED,
	DIFF_SYMBOL_SUBMODULE_MODIFIED,
	DIFF_SYMBOL_SUBMODULE_HEADER,
	DIFF_SYMBOL_SUBMODULE_ERROR,
	DIFF_SYMBOL_SUBMODULE_PIPETHROUGH,
695
	DIFF_SYMBOL_REWRITE_DIFF,
696
	DIFF_SYMBOL_BINARY_FILES,
697
	DIFF_SYMBOL_HEADER,
698 699
	DIFF_SYMBOL_FILEPAIR_PLUS,
	DIFF_SYMBOL_FILEPAIR_MINUS,
700 701
	DIFF_SYMBOL_WORDS_PORCELAIN,
	DIFF_SYMBOL_WORDS,
702
	DIFF_SYMBOL_CONTEXT,
703
	DIFF_SYMBOL_CONTEXT_INCOMPLETE,
704 705
	DIFF_SYMBOL_PLUS,
	DIFF_SYMBOL_MINUS,
706
	DIFF_SYMBOL_NO_LF_EOF,
707
	DIFF_SYMBOL_CONTEXT_FRAGINFO,
708
	DIFF_SYMBOL_CONTEXT_MARKER,
709 710
	DIFF_SYMBOL_SEPARATOR
};
711 712 713 714 715 716
/*
 * Flags for content lines:
 * 0..12 are whitespace rules
 * 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT
 * 16 is marking if the line is blank at EOF
 */
717 718 719
#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF	(1<<16)
#define DIFF_SYMBOL_MOVED_LINE			(1<<17)
#define DIFF_SYMBOL_MOVED_LINE_ALT		(1<<18)
720
#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING	(1<<19)
721 722
#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
/*
 * This struct is used when we need to buffer the output of the diff output.
 *
 * NEEDSWORK: Instead of storing a copy of the line, add an offset pointer
 * into the pre/post image file. This pointer could be a union with the
 * line pointer. By storing an offset into the file instead of the literal line,
 * we can decrease the memory footprint for the buffered output. At first we
 * may want to only have indirection for the content lines, but we could also
 * enhance the state for emitting prefabricated lines, e.g. the similarity
 * score line or hunk/file headers would only need to store a number or path
 * and then the output can be constructed later on depending on state.
 */
struct emitted_diff_symbol {
	const char *line;
	int len;
	int flags;
	enum diff_symbol s;
};
#define EMITTED_DIFF_SYMBOL_INIT {NULL}

struct emitted_diff_symbols {
	struct emitted_diff_symbol *buf;
	int nr, alloc;
};
#define EMITTED_DIFF_SYMBOLS_INIT {NULL, 0, 0}

static void append_emitted_diff_symbol(struct diff_options *o,
				       struct emitted_diff_symbol *e)
J
Junio C Hamano 已提交
751
{
752 753 754 755 756 757 758 759 760
	struct emitted_diff_symbol *f;

	ALLOC_GROW(o->emitted_symbols->buf,
		   o->emitted_symbols->nr + 1,
		   o->emitted_symbols->alloc);
	f = &o->emitted_symbols->buf[o->emitted_symbols->nr++];

	memcpy(f, e, sizeof(struct emitted_diff_symbol));
	f->line = e->line ? xmemdupz(e->line, e->len) : NULL;
J
Junio C Hamano 已提交
761 762
}

763 764 765 766
struct moved_entry {
	struct hashmap_entry ent;
	const struct emitted_diff_symbol *es;
	struct moved_entry *next_line;
767
	struct ws_delta *wsd;
768 769
};

770 771 772 773 774 775 776 777 778 779 780
/**
 * The struct ws_delta holds white space differences between moved lines, i.e.
 * between '+' and '-' lines that have been detected to be a move.
 * The string contains the difference in leading white spaces, before the
 * rest of the line is compared using the white space config for move
 * coloring. The current_longer indicates if the first string in the
 * comparision is longer than the second.
 */
struct ws_delta {
	char *string;
	unsigned int current_longer : 1;
781
};
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
#define WS_DELTA_INIT { NULL, 0 }

static int compute_ws_delta(const struct emitted_diff_symbol *a,
			     const struct emitted_diff_symbol *b,
			     struct ws_delta *out)
{
	const struct emitted_diff_symbol *longer =  a->len > b->len ? a : b;
	const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
	int d = longer->len - shorter->len;

	out->string = xmemdupz(longer->line, d);
	out->current_longer = (a == longer);

	return !strncmp(longer->line + d, shorter->line, shorter->len);
}

static int cmp_in_block_with_wsd(const struct diff_options *o,
				 const struct moved_entry *cur,
				 const struct moved_entry *match,
				 struct moved_entry *pmb,
				 int n)
{
	struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
	int al = cur->es->len, cl = l->len;
	const char *a = cur->es->line,
		   *b = match->es->line,
		   *c = l->line;

	int wslen;

	/*
	 * We need to check if 'cur' is equal to 'match'.
	 * As those are from the same (+/-) side, we do not need to adjust for
	 * indent changes. However these were found using fuzzy matching
	 * so we do have to check if they are equal.
	 */
	if (strcmp(a, b))
		return 1;

	if (!pmb->wsd)
		/*
		 * No white space delta was carried forward? This can happen
		 * when we exit early in this function and do not carry
		 * forward ws.
		 */
		return 1;

	/*
	 * The indent changes of the block are known and carried forward in
	 * pmb->wsd; however we need to check if the indent changes of the
	 * current line are still the same as before.
	 *
	 * To do so we need to compare 'l' to 'cur', adjusting the
	 * one of them for the white spaces, depending which was longer.
	 */

	wslen = strlen(pmb->wsd->string);
	if (pmb->wsd->current_longer) {
		c += wslen;
		cl -= wslen;
	} else {
		a += wslen;
		al -= wslen;
	}

	if (strcmp(a, c))
		return 1;

	return 0;
}
852

853 854 855
static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
			   const void *entry,
			   const void *entry_or_key,
856 857
			   const void *keydata)
{
858 859 860
	const struct diff_options *diffopt = hashmap_cmp_fn_data;
	const struct moved_entry *a = entry;
	const struct moved_entry *b = entry_or_key;
861 862
	unsigned flags = diffopt->color_moved_ws_handling
			 & XDF_WHITESPACE_FLAGS;
863

864 865 866 867 868 869 870 871 872 873
	if (diffopt->color_moved_ws_handling &
	    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
		/*
		 * As there is not specific white space config given,
		 * we'd need to check for a new block, so ignore all
		 * white space. The setup of the white space
		 * configuration for the next block is done else where
		 */
		flags |= XDF_IGNORE_WHITESPACE;

874 875
	return !xdiff_compare_lines(a->es->line, a->es->len,
				    b->es->line, b->es->len,
876
				    flags);
877 878 879 880 881 882 883
}

static struct moved_entry *prepare_entry(struct diff_options *o,
					 int line_no)
{
	struct moved_entry *ret = xmalloc(sizeof(*ret));
	struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
884
	unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
885

886
	ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
887 888
	ret->es = l;
	ret->next_line = NULL;
889
	ret->wsd = NULL;
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925

	return ret;
}

static void add_lines_to_move_detection(struct diff_options *o,
					struct hashmap *add_lines,
					struct hashmap *del_lines)
{
	struct moved_entry *prev_line = NULL;

	int n;
	for (n = 0; n < o->emitted_symbols->nr; n++) {
		struct hashmap *hm;
		struct moved_entry *key;

		switch (o->emitted_symbols->buf[n].s) {
		case DIFF_SYMBOL_PLUS:
			hm = add_lines;
			break;
		case DIFF_SYMBOL_MINUS:
			hm = del_lines;
			break;
		default:
			prev_line = NULL;
			continue;
		}

		key = prepare_entry(o, n);
		if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
			prev_line->next_line = key;

		hashmap_add(hm, key);
		prev_line = key;
	}
}

926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
static void pmb_advance_or_null(struct diff_options *o,
				struct moved_entry *match,
				struct hashmap *hm,
				struct moved_entry **pmb,
				int pmb_nr)
{
	int i;
	for (i = 0; i < pmb_nr; i++) {
		struct moved_entry *prev = pmb[i];
		struct moved_entry *cur = (prev && prev->next_line) ?
				prev->next_line : NULL;
		if (cur && !hm->cmpfn(o, cur, match, NULL)) {
			pmb[i] = cur;
		} else {
			pmb[i] = NULL;
		}
	}
}

945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
static void pmb_advance_or_null_multi_match(struct diff_options *o,
					    struct moved_entry *match,
					    struct hashmap *hm,
					    struct moved_entry **pmb,
					    int pmb_nr, int n)
{
	int i;
	char *got_match = xcalloc(1, pmb_nr);

	for (; match; match = hashmap_get_next(hm, match)) {
		for (i = 0; i < pmb_nr; i++) {
			struct moved_entry *prev = pmb[i];
			struct moved_entry *cur = (prev && prev->next_line) ?
					prev->next_line : NULL;
			if (!cur)
				continue;
			if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
				got_match[i] |= 1;
		}
	}

	for (i = 0; i < pmb_nr; i++) {
		if (got_match[i]) {
			/* Carry the white space delta forward */
			pmb[i]->next_line->wsd = pmb[i]->wsd;
			pmb[i] = pmb[i]->next_line;
		} else
			pmb[i] = NULL;
	}
}

976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
static int shrink_potential_moved_blocks(struct moved_entry **pmb,
					 int pmb_nr)
{
	int lp, rp;

	/* Shrink the set of potential block to the remaining running */
	for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
		while (lp < pmb_nr && pmb[lp])
			lp++;
		/* lp points at the first NULL now */

		while (rp > -1 && !pmb[rp])
			rp--;
		/* rp points at the last non-NULL */

		if (lp < pmb_nr && rp > -1 && lp < rp) {
			pmb[lp] = pmb[rp];
993 994 995 996
			if (pmb[rp]->wsd) {
				free(pmb[rp]->wsd->string);
				FREE_AND_NULL(pmb[rp]->wsd);
			}
997 998 999 1000 1001 1002 1003 1004 1005 1006
			pmb[rp] = NULL;
			rp--;
			lp++;
		}
	}

	/* Remember the number of running sets */
	return rp + 1;
}

1007 1008 1009
/*
 * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing.
 *
1010 1011
 * Otherwise, if the last block has fewer alphanumeric characters than
 * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in
1012 1013 1014 1015
 * that block.
 *
 * The last block consists of the (n - block_length)'th line up to but not
 * including the nth line.
1016 1017 1018
 *
 * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
 * Think of a way to unify them.
1019 1020 1021
 */
static void adjust_last_block(struct diff_options *o, int n, int block_length)
{
1022 1023
	int i, alnum_count = 0;
	if (o->color_moved == COLOR_MOVED_PLAIN)
1024
		return;
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
	for (i = 1; i < block_length + 1; i++) {
		const char *c = o->emitted_symbols->buf[n - i].line;
		for (; *c; c++) {
			if (!isalnum(*c))
				continue;
			alnum_count++;
			if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
				return;
		}
	}
1035 1036 1037 1038
	for (i = 1; i < block_length + 1; i++)
		o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
}

1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
/* Find blocks of moved code, delegate actual coloring decision to helper */
static void mark_color_as_moved(struct diff_options *o,
				struct hashmap *add_lines,
				struct hashmap *del_lines)
{
	struct moved_entry **pmb = NULL; /* potentially moved blocks */
	int pmb_nr = 0, pmb_alloc = 0;
	int n, flipped_block = 1, block_length = 0;


	for (n = 0; n < o->emitted_symbols->nr; n++) {
		struct hashmap *hm = NULL;
		struct moved_entry *key;
		struct moved_entry *match = NULL;
		struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];

		switch (l->s) {
		case DIFF_SYMBOL_PLUS:
			hm = del_lines;
			key = prepare_entry(o, n);
1059
			match = hashmap_get(hm, key, NULL);
1060 1061 1062 1063 1064
			free(key);
			break;
		case DIFF_SYMBOL_MINUS:
			hm = add_lines;
			key = prepare_entry(o, n);
1065
			match = hashmap_get(hm, key, NULL);
1066 1067 1068 1069 1070 1071 1072
			free(key);
			break;
		default:
			flipped_block = 1;
		}

		if (!match) {
1073
			adjust_last_block(o, n, block_length);
1074 1075 1076 1077 1078 1079 1080
			pmb_nr = 0;
			block_length = 0;
			continue;
		}

		l->flags |= DIFF_SYMBOL_MOVED_LINE;

1081 1082 1083
		if (o->color_moved == COLOR_MOVED_PLAIN)
			continue;

1084 1085 1086 1087 1088
		if (o->color_moved_ws_handling &
		    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
			pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
		else
			pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098

		pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);

		if (pmb_nr == 0) {
			/*
			 * The current line is the start of a new block.
			 * Setup the set of potential blocks.
			 */
			for (; match; match = hashmap_get_next(hm, match)) {
				ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
				if (o->color_moved_ws_handling &
				    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
					struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
					if (compute_ws_delta(l, match->es, wsd)) {
						match->wsd = wsd;
						pmb[pmb_nr++] = match;
					} else
						free(wsd);
				} else {
					pmb[pmb_nr++] = match;
				}
1110 1111 1112
			}

			flipped_block = (flipped_block + 1) % 2;
1113 1114 1115

			adjust_last_block(o, n, block_length);
			block_length = 0;
1116 1117
		}

1118 1119
		block_length++;

1120
		if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
1121 1122
			l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
	}
1123
	adjust_last_block(o, n, block_length);
1124 1125 1126

	free(pmb);
}
1127

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
  (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
static void dim_moved_lines(struct diff_options *o)
{
	int n;
	for (n = 0; n < o->emitted_symbols->nr; n++) {
		struct emitted_diff_symbol *prev = (n != 0) ?
				&o->emitted_symbols->buf[n - 1] : NULL;
		struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
		struct emitted_diff_symbol *next =
				(n < o->emitted_symbols->nr - 1) ?
				&o->emitted_symbols->buf[n + 1] : NULL;

		/* Not a plus or minus line? */
		if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS)
			continue;

		/* Not a moved line? */
		if (!(l->flags & DIFF_SYMBOL_MOVED_LINE))
			continue;

		/*
		 * If prev or next are not a plus or minus line,
		 * pretend they don't exist
		 */
		if (prev && prev->s != DIFF_SYMBOL_PLUS &&
			    prev->s != DIFF_SYMBOL_MINUS)
			prev = NULL;
		if (next && next->s != DIFF_SYMBOL_PLUS &&
			    next->s != DIFF_SYMBOL_MINUS)
			next = NULL;

		/* Inside a block? */
		if ((prev &&
		    (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
		    (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) &&
		    (next &&
		    (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
		    (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) {
			l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
			continue;
		}

		/* Check if we are at an interesting bound: */
		if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) &&
		    (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
		       (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
			continue;
		if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) &&
		    (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
		       (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
			continue;

		/*
		 * The boundary to prev and next are not interesting,
		 * so this line is not interesting as a whole
		 */
		l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
	}
}

1189 1190
static void emit_line_ws_markup(struct diff_options *o,
				const char *set, const char *reset,
1191 1192
				const char *line, int len,
				const char *set_sign, char sign,
1193
				unsigned ws_rule, int blank_at_eof)
J
Junio C Hamano 已提交
1194
{
1195
	const char *ws = NULL;
J
Junio C Hamano 已提交
1196

1197 1198
	if (o->ws_error_highlight & ws_rule) {
		ws = diff_get_color_opt(o, DIFF_WHITESPACE);
1199 1200 1201 1202
		if (!*ws)
			ws = NULL;
	}

1203 1204 1205 1206 1207 1208 1209 1210
	if (!ws && !set_sign)
		emit_line_0(o, set, 0, reset, sign, line, len);
	else if (!ws) {
		/* Emit just the prefix, then the rest. */
		emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
			    sign, "", 0);
		emit_line_0(o, set, 0, reset, 0, line, len);
	} else if (blank_at_eof)
J
Junio C Hamano 已提交
1211
		/* Blank line at EOF - paint '+' as well */
1212
		emit_line_0(o, ws, 0, reset, sign, line, len);
J
Junio C Hamano 已提交
1213 1214
	else {
		/* Emit just the prefix, then the rest. */
1215 1216
		emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
			    sign, "", 0);
1217 1218
		ws_check_emit(line, len, ws_rule,
			      o->file, set, reset, ws);
J
Junio C Hamano 已提交
1219 1220 1221
	}
}

1222 1223
static void emit_diff_symbol_from_struct(struct diff_options *o,
					 struct emitted_diff_symbol *eds)
1224
{
1225
	static const char *nneof = " No newline at end of file\n";
1226
	const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
1227
	struct strbuf sb = STRBUF_INIT;
1228 1229 1230 1231 1232 1233

	enum diff_symbol s = eds->s;
	const char *line = eds->line;
	int len = eds->len;
	unsigned flags = eds->flags;

1234
	switch (s) {
1235 1236 1237 1238
	case DIFF_SYMBOL_NO_LF_EOF:
		context = diff_get_color_opt(o, DIFF_CONTEXT);
		reset = diff_get_color_opt(o, DIFF_RESET);
		putc('\n', o->file);
1239
		emit_line_0(o, context, 0, reset, '\\',
1240 1241
			    nneof, strlen(nneof));
		break;
1242 1243 1244
	case DIFF_SYMBOL_SUBMODULE_HEADER:
	case DIFF_SYMBOL_SUBMODULE_ERROR:
	case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH:
1245
	case DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES:
1246
	case DIFF_SYMBOL_SUMMARY:
1247
	case DIFF_SYMBOL_STATS_LINE:
1248
	case DIFF_SYMBOL_BINARY_DIFF_BODY:
1249 1250 1251
	case DIFF_SYMBOL_CONTEXT_FRAGINFO:
		emit_line(o, "", "", line, len);
		break;
1252
	case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
1253 1254 1255 1256 1257
	case DIFF_SYMBOL_CONTEXT_MARKER:
		context = diff_get_color_opt(o, DIFF_CONTEXT);
		reset = diff_get_color_opt(o, DIFF_RESET);
		emit_line(o, context, reset, line, len);
		break;
1258 1259 1260 1261 1262
	case DIFF_SYMBOL_SEPARATOR:
		fprintf(o->file, "%s%c",
			diff_line_prefix(o),
			o->line_termination);
		break;
1263 1264 1265
	case DIFF_SYMBOL_CONTEXT:
		set = diff_get_color_opt(o, DIFF_CONTEXT);
		reset = diff_get_color_opt(o, DIFF_RESET);
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
		set_sign = NULL;
		if (o->flags.dual_color_diffed_diffs) {
			char c = !len ? 0 : line[0];

			if (c == '+')
				set = diff_get_color_opt(o, DIFF_FILE_NEW);
			else if (c == '@')
				set = diff_get_color_opt(o, DIFF_FRAGINFO);
			else if (c == '-')
				set = diff_get_color_opt(o, DIFF_FILE_OLD);
		}
		emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
1278 1279 1280
				    flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
		break;
	case DIFF_SYMBOL_PLUS:
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290
		switch (flags & (DIFF_SYMBOL_MOVED_LINE |
				 DIFF_SYMBOL_MOVED_LINE_ALT |
				 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_ALT |
		     DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
			set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM);
			break;
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_ALT:
1291
			set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT);
1292 1293 1294 1295 1296 1297
			break;
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
			set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM);
			break;
		case DIFF_SYMBOL_MOVED_LINE:
1298
			set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED);
1299 1300
			break;
		default:
1301
			set = diff_get_color_opt(o, DIFF_FILE_NEW);
1302
		}
1303
		reset = diff_get_color_opt(o, DIFF_RESET);
1304 1305 1306 1307 1308 1309 1310
		if (!o->flags.dual_color_diffed_diffs)
			set_sign = NULL;
		else {
			char c = !len ? 0 : line[0];

			set_sign = set;
			if (c == '-')
1311
				set = diff_get_color_opt(o, DIFF_FILE_OLD_BOLD);
1312 1313
			else if (c == '@')
				set = diff_get_color_opt(o, DIFF_FRAGINFO);
1314 1315 1316 1317
			else if (c == '+')
				set = diff_get_color_opt(o, DIFF_FILE_NEW_BOLD);
			else
				set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD);
1318
			flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
1319 1320
		}
		emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
1321 1322 1323 1324
				    flags & DIFF_SYMBOL_CONTENT_WS_MASK,
				    flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
		break;
	case DIFF_SYMBOL_MINUS:
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
		switch (flags & (DIFF_SYMBOL_MOVED_LINE |
				 DIFF_SYMBOL_MOVED_LINE_ALT |
				 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_ALT |
		     DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
			set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM);
			break;
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_ALT:
1335
			set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT);
1336 1337 1338 1339 1340 1341
			break;
		case DIFF_SYMBOL_MOVED_LINE |
		     DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
			set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM);
			break;
		case DIFF_SYMBOL_MOVED_LINE:
1342
			set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED);
1343 1344
			break;
		default:
1345
			set = diff_get_color_opt(o, DIFF_FILE_OLD);
1346
		}
1347
		reset = diff_get_color_opt(o, DIFF_RESET);
1348 1349 1350 1351 1352 1353 1354
		if (!o->flags.dual_color_diffed_diffs)
			set_sign = NULL;
		else {
			char c = !len ? 0 : line[0];

			set_sign = set;
			if (c == '+')
1355
				set = diff_get_color_opt(o, DIFF_FILE_NEW_DIM);
1356 1357
			else if (c == '@')
				set = diff_get_color_opt(o, DIFF_FRAGINFO);
1358 1359 1360 1361
			else if (c == '-')
				set = diff_get_color_opt(o, DIFF_FILE_OLD_DIM);
			else
				set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
1362 1363
		}
		emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
1364 1365
				    flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
		break;
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385
	case DIFF_SYMBOL_WORDS_PORCELAIN:
		context = diff_get_color_opt(o, DIFF_CONTEXT);
		reset = diff_get_color_opt(o, DIFF_RESET);
		emit_line(o, context, reset, line, len);
		fputs("~\n", o->file);
		break;
	case DIFF_SYMBOL_WORDS:
		context = diff_get_color_opt(o, DIFF_CONTEXT);
		reset = diff_get_color_opt(o, DIFF_RESET);
		/*
		 * Skip the prefix character, if any.  With
		 * diff_suppress_blank_empty, there may be
		 * none.
		 */
		if (line[0] != '\n') {
			line++;
			len--;
		}
		emit_line(o, context, reset, line, len);
		break;
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
	case DIFF_SYMBOL_FILEPAIR_PLUS:
		meta = diff_get_color_opt(o, DIFF_METAINFO);
		reset = diff_get_color_opt(o, DIFF_RESET);
		fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta,
			line, reset,
			strchr(line, ' ') ? "\t" : "");
		break;
	case DIFF_SYMBOL_FILEPAIR_MINUS:
		meta = diff_get_color_opt(o, DIFF_METAINFO);
		reset = diff_get_color_opt(o, DIFF_RESET);
		fprintf(o->file, "%s%s--- %s%s%s\n", diff_line_prefix(o), meta,
			line, reset,
			strchr(line, ' ') ? "\t" : "");
		break;
1400
	case DIFF_SYMBOL_BINARY_FILES:
1401 1402 1403
	case DIFF_SYMBOL_HEADER:
		fprintf(o->file, "%s", line);
		break;
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
	case DIFF_SYMBOL_BINARY_DIFF_HEADER:
		fprintf(o->file, "%sGIT binary patch\n", diff_line_prefix(o));
		break;
	case DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA:
		fprintf(o->file, "%sdelta %s\n", diff_line_prefix(o), line);
		break;
	case DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL:
		fprintf(o->file, "%sliteral %s\n", diff_line_prefix(o), line);
		break;
	case DIFF_SYMBOL_BINARY_DIFF_FOOTER:
		fputs(diff_line_prefix(o), o->file);
		fputc('\n', o->file);
		break;
1417 1418 1419 1420 1421
	case DIFF_SYMBOL_REWRITE_DIFF:
		fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
		reset = diff_get_color_opt(o, DIFF_RESET);
		emit_line(o, fraginfo, reset, line, len);
		break;
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
	case DIFF_SYMBOL_SUBMODULE_ADD:
		set = diff_get_color_opt(o, DIFF_FILE_NEW);
		reset = diff_get_color_opt(o, DIFF_RESET);
		emit_line(o, set, reset, line, len);
		break;
	case DIFF_SYMBOL_SUBMODULE_DEL:
		set = diff_get_color_opt(o, DIFF_FILE_OLD);
		reset = diff_get_color_opt(o, DIFF_RESET);
		emit_line(o, set, reset, line, len);
		break;
	case DIFF_SYMBOL_SUBMODULE_UNTRACKED:
		fprintf(o->file, "%sSubmodule %s contains untracked content\n",
			diff_line_prefix(o), line);
		break;
	case DIFF_SYMBOL_SUBMODULE_MODIFIED:
		fprintf(o->file, "%sSubmodule %s contains modified content\n",
			diff_line_prefix(o), line);
		break;
1440 1441 1442 1443 1444 1445 1446
	case DIFF_SYMBOL_STATS_SUMMARY_NO_FILES:
		emit_line(o, "", "", " 0 files changed\n",
			  strlen(" 0 files changed\n"));
		break;
	case DIFF_SYMBOL_STATS_SUMMARY_ABBREV:
		emit_line(o, "", "", " ...\n", strlen(" ...\n"));
		break;
1447 1448 1449
	case DIFF_SYMBOL_WORD_DIFF:
		fprintf(o->file, "%.*s", len, line);
		break;
1450 1451 1452
	case DIFF_SYMBOL_STAT_SEP:
		fputs(o->stat_sep, o->file);
		break;
1453
	default:
1454
		BUG("unknown diff symbol");
1455
	}
1456
	strbuf_release(&sb);
1457 1458
}

1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
			     const char *line, int len, unsigned flags)
{
	struct emitted_diff_symbol e = {line, len, flags, s};

	if (o->emitted_symbols)
		append_emitted_diff_symbol(o, &e);
	else
		emit_diff_symbol_from_struct(o, &e);
}

1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508
void diff_emit_submodule_del(struct diff_options *o, const char *line)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_DEL, line, strlen(line), 0);
}

void diff_emit_submodule_add(struct diff_options *o, const char *line)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ADD, line, strlen(line), 0);
}

void diff_emit_submodule_untracked(struct diff_options *o, const char *path)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_UNTRACKED,
			 path, strlen(path), 0);
}

void diff_emit_submodule_modified(struct diff_options *o, const char *path)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_MODIFIED,
			 path, strlen(path), 0);
}

void diff_emit_submodule_header(struct diff_options *o, const char *header)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_HEADER,
			 header, strlen(header), 0);
}

void diff_emit_submodule_error(struct diff_options *o, const char *err)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ERROR, err, strlen(err), 0);
}

void diff_emit_submodule_pipethrough(struct diff_options *o,
				     const char *line, int len)
{
	emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, line, len, 0);
}

J
Junio C Hamano 已提交
1509 1510 1511 1512 1513 1514 1515 1516
static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
{
	if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
	      ecbdata->blank_at_eof_in_preimage &&
	      ecbdata->blank_at_eof_in_postimage &&
	      ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
	      ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
		return 0;
1517
	return ws_blank_line(line, len, ecbdata->ws_rule);
J
Junio C Hamano 已提交
1518 1519
}

1520
static void emit_add_line(const char *reset,
1521 1522 1523
			  struct emit_callback *ecbdata,
			  const char *line, int len)
{
1524 1525 1526 1527 1528
	unsigned flags = WSEH_NEW | ecbdata->ws_rule;
	if (new_blank_line_at_eof(ecbdata, line, len))
		flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF;

	emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags);
1529
}
1530

1531 1532 1533 1534
static void emit_del_line(const char *reset,
			  struct emit_callback *ecbdata,
			  const char *line, int len)
{
1535 1536
	unsigned flags = WSEH_OLD | ecbdata->ws_rule;
	emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags);
1537 1538 1539 1540 1541 1542
}

static void emit_context_line(const char *reset,
			      struct emit_callback *ecbdata,
			      const char *line, int len)
{
1543 1544
	unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule;
	emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags);
1545 1546
}

B
Bert Wesarg 已提交
1547 1548 1549
static void emit_hunk_header(struct emit_callback *ecbdata,
			     const char *line, int len)
{
1550
	const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
B
Bert Wesarg 已提交
1551 1552 1553
	const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
	const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
	const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1554
	const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
B
Bert Wesarg 已提交
1555 1556
	static const char atat[2] = { '@', '@' };
	const char *cp, *ep;
B
Bo Yang 已提交
1557 1558 1559
	struct strbuf msgbuf = STRBUF_INIT;
	int org_len = len;
	int i = 1;
B
Bert Wesarg 已提交
1560 1561 1562 1563 1564 1565 1566 1567

	/*
	 * As a hunk header must begin with "@@ -<old>, +<new> @@",
	 * it always is at least 10 bytes long.
	 */
	if (len < 10 ||
	    memcmp(line, atat, 2) ||
	    !(ep = memmem(line + 2, len - 2, atat, 2))) {
1568
		emit_diff_symbol(ecbdata->opt,
1569
				 DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0);
B
Bert Wesarg 已提交
1570 1571 1572 1573 1574
		return;
	}
	ep += 2; /* skip over @@ */

	/* The hunk header in fraginfo color */
1575 1576
	if (ecbdata->opt->flags.dual_color_diffed_diffs)
		strbuf_addstr(&msgbuf, reverse);
1577
	strbuf_addstr(&msgbuf, frag);
B
Bo Yang 已提交
1578
	strbuf_add(&msgbuf, line, ep - line);
1579
	strbuf_addstr(&msgbuf, reset);
B
Bo Yang 已提交
1580 1581 1582 1583 1584 1585 1586

	/*
	 * trailing "\r\n"
	 */
	for ( ; i < 3; i++)
		if (line[len - i] == '\r' || line[len - i] == '\n')
			len--;
B
Bert Wesarg 已提交
1587 1588 1589 1590 1591

	/* blank before the func header */
	for (cp = ep; ep - line < len; ep++)
		if (*ep != ' ' && *ep != '\t')
			break;
B
Bo Yang 已提交
1592
	if (ep != cp) {
1593
		strbuf_addstr(&msgbuf, context);
B
Bo Yang 已提交
1594
		strbuf_add(&msgbuf, cp, ep - cp);
1595
		strbuf_addstr(&msgbuf, reset);
B
Bo Yang 已提交
1596 1597 1598
	}

	if (ep < line + len) {
1599
		strbuf_addstr(&msgbuf, func);
B
Bo Yang 已提交
1600
		strbuf_add(&msgbuf, ep, line + len - ep);
1601
		strbuf_addstr(&msgbuf, reset);
B
Bo Yang 已提交
1602
	}
B
Bert Wesarg 已提交
1603

B
Bo Yang 已提交
1604
	strbuf_add(&msgbuf, line + len, org_len - len);
1605
	strbuf_complete_line(&msgbuf);
1606
	emit_diff_symbol(ecbdata->opt,
1607
			 DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0);
B
Bo Yang 已提交
1608
	strbuf_release(&msgbuf);
B
Bert Wesarg 已提交
1609 1610
}

1611 1612 1613 1614 1615
static struct diff_tempfile *claim_diff_tempfile(void) {
	int i;
	for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
		if (!diff_temp[i].name)
			return diff_temp + i;
1616
	BUG("diff is failing to clean up its tempfiles");
1617 1618 1619 1620 1621
}

static void remove_tempfile(void)
{
	int i;
1622
	for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
1623
		if (is_tempfile_active(diff_temp[i].tempfile))
M
Michael Haggerty 已提交
1624
			delete_tempfile(&diff_temp[i].tempfile);
1625 1626
		diff_temp[i].name = NULL;
	}
1627 1628
}

1629
static void add_line_count(struct strbuf *out, int count)
J
Junio C Hamano 已提交
1630 1631 1632
{
	switch (count) {
	case 0:
1633
		strbuf_addstr(out, "0,0");
J
Junio C Hamano 已提交
1634 1635
		break;
	case 1:
1636
		strbuf_addstr(out, "1");
J
Junio C Hamano 已提交
1637 1638
		break;
	default:
1639
		strbuf_addf(out, "1,%d", count);
J
Junio C Hamano 已提交
1640 1641 1642 1643
		break;
	}
}

1644 1645
static void emit_rewrite_lines(struct emit_callback *ecb,
			       int prefix, const char *data, int size)
J
Junio C Hamano 已提交
1646
{
1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
	const char *endp = NULL;
	const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);

	while (0 < size) {
		int len;

		endp = memchr(data, '\n', size);
		len = endp ? (endp - data + 1) : size;
		if (prefix != '+') {
			ecb->lno_in_preimage++;
1657
			emit_del_line(reset, ecb, data, len);
1658 1659 1660
		} else {
			ecb->lno_in_postimage++;
			emit_add_line(reset, ecb, data, len);
1661
		}
1662 1663 1664
		size -= len;
		data += len;
	}
1665
	if (!endp)
1666
		emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0);
J
Junio C Hamano 已提交
1667 1668 1669 1670 1671
}

static void emit_rewrite_diff(const char *name_a,
			      const char *name_b,
			      struct diff_filespec *one,
1672
			      struct diff_filespec *two,
J
Jeff King 已提交
1673 1674
			      struct userdiff_driver *textconv_one,
			      struct userdiff_driver *textconv_two,
1675
			      struct diff_options *o)
J
Junio C Hamano 已提交
1676 1677
{
	int lc_a, lc_b;
J
Junio C Hamano 已提交
1678
	static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
1679
	const char *a_prefix, *b_prefix;
1680
	char *data_one, *data_two;
1681
	size_t size_one, size_two;
1682
	struct emit_callback ecbdata;
1683
	struct strbuf out = STRBUF_INIT;
1684

1685
	if (diff_mnemonic_prefix && o->flags.reverse_diff) {
1686 1687 1688 1689 1690 1691
		a_prefix = o->b_prefix;
		b_prefix = o->a_prefix;
	} else {
		a_prefix = o->a_prefix;
		b_prefix = o->b_prefix;
	}
1692

J
Junio C Hamano 已提交
1693 1694
	name_a += (*name_a == '/');
	name_b += (*name_b == '/');
1695

J
Junio C Hamano 已提交
1696 1697
	strbuf_reset(&a_name);
	strbuf_reset(&b_name);
1698 1699
	quote_two_c_style(&a_name, a_prefix, name_a, 0);
	quote_two_c_style(&b_name, b_prefix, name_b, 0);
J
Junio C Hamano 已提交
1700

1701 1702
	size_one = fill_textconv(textconv_one, one, &data_one);
	size_two = fill_textconv(textconv_two, two, &data_two);
1703

1704
	memset(&ecbdata, 0, sizeof(ecbdata));
1705
	ecbdata.color_diff = want_color(o->use_color);
1706
	ecbdata.ws_rule = whitespace_rule(name_b);
1707
	ecbdata.opt = o;
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718
	if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
		mmfile_t mf1, mf2;
		mf1.ptr = (char *)data_one;
		mf2.ptr = (char *)data_two;
		mf1.size = size_one;
		mf2.size = size_two;
		check_blank_at_eof(&mf1, &mf2, &ecbdata);
	}
	ecbdata.lno_in_preimage = 1;
	ecbdata.lno_in_postimage = 1;

1719 1720
	lc_a = count_lines(data_one, size_one);
	lc_b = count_lines(data_two, size_two);
1721 1722 1723 1724 1725 1726

	emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
			 a_name.buf, a_name.len, 0);
	emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
			 b_name.buf, b_name.len, 0);

1727
	strbuf_addstr(&out, "@@ -");
1728
	if (!o->irreversible_delete)
1729
		add_line_count(&out, lc_a);
1730
	else
1731 1732 1733 1734 1735 1736 1737
		strbuf_addstr(&out, "?,?");
	strbuf_addstr(&out, " +");
	add_line_count(&out, lc_b);
	strbuf_addstr(&out, " @@\n");
	emit_diff_symbol(o, DIFF_SYMBOL_REWRITE_DIFF, out.buf, out.len, 0);
	strbuf_release(&out);

1738
	if (lc_a && !o->irreversible_delete)
1739
		emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
J
Junio C Hamano 已提交
1740
	if (lc_b)
1741
		emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
1742
	if (textconv_one)
1743
		free((char *)data_one);
1744
	if (textconv_two)
1745
		free((char *)data_two);
J
Junio C Hamano 已提交
1746 1747
}

1748 1749
struct diff_words_buffer {
	mmfile_t text;
1750
	unsigned long alloc;
1751 1752 1753 1754
	struct diff_words_orig {
		const char *begin, *end;
	} *orig;
	int orig_nr, orig_alloc;
1755 1756 1757 1758 1759
};

static void diff_words_append(char *line, unsigned long len,
		struct diff_words_buffer *buffer)
{
1760
	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
1761 1762 1763 1764
	line++;
	len--;
	memcpy(buffer->text.ptr + buffer->text.size, line, len);
	buffer->text.size += len;
1765
	buffer->text.ptr[buffer->text.size] = '\0';
1766 1767
}

1768
struct diff_words_style_elem {
1769 1770 1771 1772 1773 1774
	const char *prefix;
	const char *suffix;
	const char *color; /* NULL; filled in by the setup code if
			    * color is enabled */
};

1775
struct diff_words_style {
1776
	enum diff_words_type type;
B
Brandon Williams 已提交
1777
	struct diff_words_style_elem new_word, old_word, ctx;
1778 1779 1780
	const char *newline;
};

S
Stephen Boyd 已提交
1781
static struct diff_words_style diff_words_styles[] = {
1782 1783 1784 1785 1786
	{ DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
	{ DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
	{ DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
};

1787 1788
struct diff_words_data {
	struct diff_words_buffer minus, plus;
1789
	const char *current_plus;
1790 1791
	int last_minus;
	struct diff_options *opt;
1792
	regex_t *word_regex;
1793 1794
	enum diff_words_type type;
	struct diff_words_style *style;
1795 1796
};

1797
static int fn_out_diff_words_write_helper(struct diff_options *o,
1798 1799
					  struct diff_words_style_elem *st_el,
					  const char *newline,
1800
					  size_t count, const char *buf)
1801
{
1802
	int print = 0;
1803
	struct strbuf sb = STRBUF_INIT;
1804

1805 1806
	while (count) {
		char *p = memchr(buf, '\n', count);
1807
		if (print)
1808 1809
			strbuf_addstr(&sb, diff_line_prefix(o));

1810
		if (p != buf) {
1811 1812 1813 1814 1815 1816 1817 1818 1819
			const char *reset = st_el->color && *st_el->color ?
					    GIT_COLOR_RESET : NULL;
			if (st_el->color && *st_el->color)
				strbuf_addstr(&sb, st_el->color);
			strbuf_addstr(&sb, st_el->prefix);
			strbuf_add(&sb, buf, p ? p - buf : count);
			strbuf_addstr(&sb, st_el->suffix);
			if (reset)
				strbuf_addstr(&sb, reset);
1820 1821
		}
		if (!p)
1822 1823 1824
			goto out;

		strbuf_addstr(&sb, newline);
1825 1826
		count -= p + 1 - buf;
		buf = p + 1;
1827
		print = 1;
1828 1829 1830 1831 1832
		if (count) {
			emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
					 sb.buf, sb.len, 0);
			strbuf_reset(&sb);
		}
1833
	}
1834 1835 1836 1837 1838 1839

out:
	if (sb.len)
		emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
				 sb.buf, sb.len, 0);
	strbuf_release(&sb);
1840 1841 1842
	return 0;
}

1843 1844 1845
/*
 * '--color-words' algorithm can be described as:
 *
R
René Genz 已提交
1846
 *   1. collect the minus/plus lines of a diff hunk, divided into
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885
 *      minus-lines and plus-lines;
 *
 *   2. break both minus-lines and plus-lines into words and
 *      place them into two mmfile_t with one word for each line;
 *
 *   3. use xdiff to run diff on the two mmfile_t to get the words level diff;
 *
 * And for the common parts of the both file, we output the plus side text.
 * diff_words->current_plus is used to trace the current position of the plus file
 * which printed. diff_words->last_minus is used to trace the last minus word
 * printed.
 *
 * For '--graph' to work with '--color-words', we need to output the graph prefix
 * on each line of color words output. Generally, there are two conditions on
 * which we should output the prefix.
 *
 *   1. diff_words->last_minus == 0 &&
 *      diff_words->current_plus == diff_words->plus.text.ptr
 *
 *      that is: the plus text must start as a new line, and if there is no minus
 *      word printed, a graph prefix must be printed.
 *
 *   2. diff_words->current_plus > diff_words->plus.text.ptr &&
 *      *(diff_words->current_plus - 1) == '\n'
 *
 *      that is: a graph prefix must be printed following a '\n'
 */
static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
{
	if ((diff_words->last_minus == 0 &&
		diff_words->current_plus == diff_words->plus.text.ptr) ||
		(diff_words->current_plus > diff_words->plus.text.ptr &&
		*(diff_words->current_plus - 1) == '\n')) {
		return 1;
	} else {
		return 0;
	}
}

1886 1887 1888
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
	struct diff_words_data *diff_words = priv;
1889
	struct diff_words_style *style = diff_words->style;
1890 1891
	int minus_first, minus_len, plus_first, plus_len;
	const char *minus_begin, *minus_end, *plus_begin, *plus_end;
1892
	struct diff_options *opt = diff_words->opt;
1893
	const char *line_prefix;
1894

1895 1896
	if (line[0] != '@' || parse_hunk_header(line, len,
			&minus_first, &minus_len, &plus_first, &plus_len))
1897 1898
		return;

1899
	assert(opt);
1900
	line_prefix = diff_line_prefix(opt);
1901

1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916
	/* POSIX requires that first be decremented by one if len == 0... */
	if (minus_len) {
		minus_begin = diff_words->minus.orig[minus_first].begin;
		minus_end =
			diff_words->minus.orig[minus_first + minus_len - 1].end;
	} else
		minus_begin = minus_end =
			diff_words->minus.orig[minus_first].end;

	if (plus_len) {
		plus_begin = diff_words->plus.orig[plus_first].begin;
		plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
	} else
		plus_begin = plus_end = diff_words->plus.orig[plus_first].end;

1917 1918 1919 1920
	if (color_words_output_graph_prefix(diff_words)) {
		fputs(line_prefix, diff_words->opt->file);
	}
	if (diff_words->current_plus != plus_begin) {
1921
		fn_out_diff_words_write_helper(diff_words->opt,
1922 1923
				&style->ctx, style->newline,
				plus_begin - diff_words->current_plus,
1924
				diff_words->current_plus);
1925 1926
	}
	if (minus_begin != minus_end) {
1927
		fn_out_diff_words_write_helper(diff_words->opt,
B
Brandon Williams 已提交
1928
				&style->old_word, style->newline,
1929
				minus_end - minus_begin, minus_begin);
1930 1931
	}
	if (plus_begin != plus_end) {
1932
		fn_out_diff_words_write_helper(diff_words->opt,
B
Brandon Williams 已提交
1933
				&style->new_word, style->newline,
1934
				plus_end - plus_begin, plus_begin);
1935
	}
1936 1937

	diff_words->current_plus = plus_end;
1938
	diff_words->last_minus = minus_first;
1939 1940
}

1941 1942 1943 1944 1945 1946
/* This function starts looking at *begin, and returns 0 iff a word was found. */
static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
		int *begin, int *end)
{
	if (word_regex && *begin < buffer->size) {
		regmatch_t match[1];
J
Johannes Schindelin 已提交
1947 1948
		if (!regexec_buf(word_regex, buffer->ptr + *begin,
				 buffer->size - *begin, 1, match, 0)) {
1949 1950 1951 1952 1953 1954 1955
			char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
					'\n', match[0].rm_eo - match[0].rm_so);
			*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
			*begin += match[0].rm_so;
			return *begin >= *end;
		}
		return -1;
1956 1957
	}

1958 1959 1960 1961 1962
	/* find the next word */
	while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
		(*begin)++;
	if (*begin >= buffer->size)
		return -1;
1963

1964 1965 1966 1967 1968 1969
	/* find the end of the word */
	*end = *begin + 1;
	while (*end < buffer->size && !isspace(buffer->ptr[*end]))
		(*end)++;

	return 0;
1970 1971
}

1972
/*
1973 1974 1975
 * This function splits the words in buffer->text, stores the list with
 * newline separator into out, and saves the offsets of the original words
 * in buffer->orig.
1976
 */
1977 1978
static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
		regex_t *word_regex)
1979
{
1980
	int i, j;
1981
	long alloc = 0;
1982

1983
	out->size = 0;
1984
	out->ptr = NULL;
1985

1986 1987 1988 1989 1990 1991
	/* fake an empty "0th" word */
	ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
	buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
	buffer->orig_nr = 1;

	for (i = 0; i < buffer->text.size; i++) {
1992 1993
		if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
			return;
1994 1995 1996 1997 1998 1999 2000 2001 2002

		/* store original boundaries */
		ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
				buffer->orig_alloc);
		buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
		buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
		buffer->orig_nr++;

		/* store one word */
2003
		ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
2004 2005 2006 2007 2008
		memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
		out->ptr[out->size + j - i] = '\n';
		out->size += j - i + 1;

		i = j - 1;
2009 2010 2011 2012 2013 2014 2015 2016 2017
	}
}

/* this executes the word diff on the accumulated buffers */
static void diff_words_show(struct diff_words_data *diff_words)
{
	xpparam_t xpp;
	xdemitconf_t xecfg;
	mmfile_t minus, plus;
2018
	struct diff_words_style *style = diff_words->style;
2019

2020
	struct diff_options *opt = diff_words->opt;
2021
	const char *line_prefix;
2022 2023

	assert(opt);
2024
	line_prefix = diff_line_prefix(opt);
2025

2026 2027
	/* special case: only removal */
	if (!diff_words->plus.text.size) {
2028 2029 2030
		emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
				 line_prefix, strlen(line_prefix), 0);
		fn_out_diff_words_write_helper(diff_words->opt,
B
Brandon Williams 已提交
2031
			&style->old_word, style->newline,
2032
			diff_words->minus.text.size,
2033
			diff_words->minus.text.ptr);
2034 2035 2036 2037 2038
		diff_words->minus.text.size = 0;
		return;
	}

	diff_words->current_plus = diff_words->plus.text.ptr;
2039
	diff_words->last_minus = 0;
2040

B
Brian Downing 已提交
2041
	memset(&xpp, 0, sizeof(xpp));
2042
	memset(&xecfg, 0, sizeof(xecfg));
2043 2044
	diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
	diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
R
René Scharfe 已提交
2045
	xpp.flags = 0;
2046
	/* as only the hunk header will be parsed, we need a 0-context */
2047
	xecfg.ctxlen = 0;
J
Jeff King 已提交
2048 2049 2050
	if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
			  &xpp, &xecfg))
		die("unable to generate word diff");
2051 2052
	free(minus.ptr);
	free(plus.ptr);
2053
	if (diff_words->current_plus != diff_words->plus.text.ptr +
2054 2055
			diff_words->plus.text.size) {
		if (color_words_output_graph_prefix(diff_words))
2056 2057 2058
			emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
					 line_prefix, strlen(line_prefix), 0);
		fn_out_diff_words_write_helper(diff_words->opt,
2059
			&style->ctx, style->newline,
2060
			diff_words->plus.text.ptr + diff_words->plus.text.size
2061
			- diff_words->current_plus, diff_words->current_plus);
2062
	}
2063 2064 2065
	diff_words->minus.text.size = diff_words->plus.text.size = 0;
}

2066 2067 2068
/* In "color-words" mode, show word-diff of words accumulated in the buffer */
static void diff_words_flush(struct emit_callback *ecbdata)
{
2069 2070
	struct diff_options *wo = ecbdata->diff_words->opt;

2071 2072 2073
	if (ecbdata->diff_words->minus.text.size ||
	    ecbdata->diff_words->plus.text.size)
		diff_words_show(ecbdata->diff_words);
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091

	if (wo->emitted_symbols) {
		struct diff_options *o = ecbdata->opt;
		struct emitted_diff_symbols *wol = wo->emitted_symbols;
		int i;

		/*
		 * NEEDSWORK:
		 * Instead of appending each, concat all words to a line?
		 */
		for (i = 0; i < wol->nr; i++)
			append_emitted_diff_symbol(o, &wol->buf[i]);

		for (i = 0; i < wol->nr; i++)
			free((void *)wol->buf[i].line);

		wol->nr = 0;
	}
2092 2093
}

2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114
static void diff_filespec_load_driver(struct diff_filespec *one)
{
	/* Use already-loaded driver */
	if (one->driver)
		return;

	if (S_ISREG(one->mode))
		one->driver = userdiff_find_by_path(one->path);

	/* Fallback to default settings */
	if (!one->driver)
		one->driver = userdiff_find_by_name("default");
}

static const char *userdiff_word_regex(struct diff_filespec *one)
{
	diff_filespec_load_driver(one);
	return one->driver->word_regex;
}

static void init_diff_words_data(struct emit_callback *ecbdata,
2115
				 struct diff_options *orig_opts,
2116 2117 2118 2119
				 struct diff_filespec *one,
				 struct diff_filespec *two)
{
	int i;
2120 2121
	struct diff_options *o = xmalloc(sizeof(struct diff_options));
	memcpy(o, orig_opts, sizeof(struct diff_options));
2122 2123 2124 2125 2126

	ecbdata->diff_words =
		xcalloc(1, sizeof(struct diff_words_data));
	ecbdata->diff_words->type = o->word_diff;
	ecbdata->diff_words->opt = o;
2127 2128 2129 2130 2131

	if (orig_opts->emitted_symbols)
		o->emitted_symbols =
			xcalloc(1, sizeof(struct emitted_diff_symbols));

2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143
	if (!o->word_regex)
		o->word_regex = userdiff_word_regex(one);
	if (!o->word_regex)
		o->word_regex = userdiff_word_regex(two);
	if (!o->word_regex)
		o->word_regex = diff_word_regex_cfg;
	if (o->word_regex) {
		ecbdata->diff_words->word_regex = (regex_t *)
			xmalloc(sizeof(regex_t));
		if (regcomp(ecbdata->diff_words->word_regex,
			    o->word_regex,
			    REG_EXTENDED | REG_NEWLINE))
2144 2145
			die("invalid regular expression: %s",
			    o->word_regex);
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155
	}
	for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
		if (o->word_diff == diff_words_styles[i].type) {
			ecbdata->diff_words->style =
				&diff_words_styles[i];
			break;
		}
	}
	if (want_color(o->use_color)) {
		struct diff_words_style *st = ecbdata->diff_words->style;
B
Brandon Williams 已提交
2156 2157
		st->old_word.color = diff_get_color_opt(o, DIFF_FILE_OLD);
		st->new_word.color = diff_get_color_opt(o, DIFF_FILE_NEW);
2158
		st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
2159 2160 2161
	}
}

2162 2163 2164
static void free_diff_words_data(struct emit_callback *ecbdata)
{
	if (ecbdata->diff_words) {
2165
		diff_words_flush(ecbdata);
2166
		free (ecbdata->diff_words->opt->emitted_symbols);
2167
		free (ecbdata->diff_words->opt);
2168
		free (ecbdata->diff_words->minus.text.ptr);
2169
		free (ecbdata->diff_words->minus.orig);
2170
		free (ecbdata->diff_words->plus.text.ptr);
2171
		free (ecbdata->diff_words->plus.orig);
2172 2173 2174 2175
		if (ecbdata->diff_words->word_regex) {
			regfree(ecbdata->diff_words->word_regex);
			free(ecbdata->diff_words->word_regex);
		}
2176
		FREE_AND_NULL(ecbdata->diff_words);
2177 2178 2179
	}
}

J
Jeff King 已提交
2180
const char *diff_get_color(int diff_use_color, enum color_diff ix)
J
Johannes Schindelin 已提交
2181
{
2182
	if (want_color(diff_use_color))
L
Linus Torvalds 已提交
2183 2184
		return diff_colors[ix];
	return "";
J
Johannes Schindelin 已提交
2185 2186
}

2187 2188 2189 2190 2191 2192 2193 2194 2195 2196
const char *diff_line_prefix(struct diff_options *opt)
{
	struct strbuf *msgbuf;
	if (!opt->output_prefix)
		return "";

	msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
	return msgbuf->buf;
}

2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212
static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
{
	const char *cp;
	unsigned long allot;
	size_t l = len;

	cp = line;
	allot = l;
	while (0 < l) {
		(void) utf8_width(&cp, &l);
		if (!cp)
			break; /* truncated in the middle? */
	}
	return allot - l;
}

2213
static void find_lno(const char *line, struct emit_callback *ecbdata)
2214
{
2215 2216 2217 2218
	const char *p;
	ecbdata->lno_in_preimage = 0;
	ecbdata->lno_in_postimage = 0;
	p = strchr(line, '-');
2219
	if (!p)
2220 2221 2222 2223 2224 2225
		return; /* cannot happen */
	ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
	p = strchr(p, '+');
	if (!p)
		return; /* cannot happen */
	ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
2226 2227
}

J
Johannes Schindelin 已提交
2228
static void fn_out_consume(void *priv, char *line, unsigned long len)
J
Junio C Hamano 已提交
2229 2230
{
	struct emit_callback *ecbdata = priv;
J
Jeff King 已提交
2231
	const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
2232
	struct diff_options *o = ecbdata->opt;
J
Junio C Hamano 已提交
2233

2234 2235
	o->found_changes = 1;

2236
	if (ecbdata->header) {
2237 2238
		emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
				 ecbdata->header->buf, ecbdata->header->len, 0);
2239 2240 2241
		strbuf_reset(ecbdata->header);
		ecbdata->header = NULL;
	}
2242

J
Junio C Hamano 已提交
2243
	if (ecbdata->label_path[0]) {
2244 2245 2246 2247 2248 2249
		emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
				 ecbdata->label_path[0],
				 strlen(ecbdata->label_path[0]), 0);
		emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
				 ecbdata->label_path[1],
				 strlen(ecbdata->label_path[1]), 0);
J
Junio C Hamano 已提交
2250 2251
		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
	}
J
Johannes Schindelin 已提交
2252

2253 2254 2255 2256 2257 2258
	if (diff_suppress_blank_empty
	    && len == 2 && line[0] == ' ' && line[1] == '\n') {
		line[0] = '\n';
		len = 1;
	}

2259
	if (line[0] == '@') {
2260 2261
		if (ecbdata->diff_words)
			diff_words_flush(ecbdata);
2262
		len = sane_truncate_line(ecbdata, line, len);
2263
		find_lno(line, ecbdata);
B
Bert Wesarg 已提交
2264
		emit_hunk_header(ecbdata, line, len);
2265
		return;
J
Johannes Schindelin 已提交
2266
	}
2267 2268

	if (ecbdata->diff_words) {
2269 2270 2271
		enum diff_symbol s =
			ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN ?
			DIFF_SYMBOL_WORDS_PORCELAIN : DIFF_SYMBOL_WORDS;
2272 2273 2274 2275 2276 2277 2278 2279
		if (line[0] == '-') {
			diff_words_append(line, len,
					  &ecbdata->diff_words->minus);
			return;
		} else if (line[0] == '+') {
			diff_words_append(line, len,
					  &ecbdata->diff_words->plus);
			return;
2280
		} else if (starts_with(line, "\\ ")) {
2281 2282 2283 2284 2285 2286 2287 2288
			/*
			 * Eat the "no newline at eof" marker as if we
			 * saw a "+" or "-" line with nothing on it,
			 * and return without diff_words_flush() to
			 * defer processing. If this is the end of
			 * preimage, more "+" lines may come after it.
			 */
			return;
2289
		}
2290
		diff_words_flush(ecbdata);
2291
		emit_diff_symbol(o, s, line, len, 0);
2292 2293 2294
		return;
	}

2295 2296
	switch (line[0]) {
	case '+':
2297
		ecbdata->lno_in_postimage++;
2298
		emit_add_line(reset, ecbdata, line + 1, len - 1);
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311
		break;
	case '-':
		ecbdata->lno_in_preimage++;
		emit_del_line(reset, ecbdata, line + 1, len - 1);
		break;
	case ' ':
		ecbdata->lno_in_postimage++;
		ecbdata->lno_in_preimage++;
		emit_context_line(reset, ecbdata, line + 1, len - 1);
		break;
	default:
		/* incomplete line at the end */
		ecbdata->lno_in_preimage++;
2312 2313
		emit_diff_symbol(o, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
				 line, len, 0);
2314
		break;
2315
	}
J
Junio C Hamano 已提交
2316 2317
}

2318
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
J
Junio C Hamano 已提交
2319
{
B
Brandon Williams 已提交
2320 2321
	const char *old_name = a;
	const char *new_name = b;
J
Junio C Hamano 已提交
2322
	int pfx_length, sfx_length;
2323
	int pfx_adjust_for_slash;
J
Junio C Hamano 已提交
2324 2325
	int len_a = strlen(a);
	int len_b = strlen(b);
2326
	int a_midlen, b_midlen;
2327 2328 2329 2330
	int qlen_a = quote_c_style(a, NULL, NULL, 0);
	int qlen_b = quote_c_style(b, NULL, NULL, 0);

	if (qlen_a || qlen_b) {
2331 2332 2333 2334
		quote_c_style(a, name, NULL, 0);
		strbuf_addstr(name, " => ");
		quote_c_style(b, name, NULL, 0);
		return;
2335
	}
J
Junio C Hamano 已提交
2336 2337 2338

	/* Find common prefix */
	pfx_length = 0;
B
Brandon Williams 已提交
2339 2340 2341 2342 2343
	while (*old_name && *new_name && *old_name == *new_name) {
		if (*old_name == '/')
			pfx_length = old_name - a + 1;
		old_name++;
		new_name++;
J
Junio C Hamano 已提交
2344 2345 2346
	}

	/* Find common suffix */
B
Brandon Williams 已提交
2347 2348
	old_name = a + len_a;
	new_name = b + len_b;
J
Junio C Hamano 已提交
2349
	sfx_length = 0;
2350
	/*
2351 2352 2353 2354 2355 2356
	 * If there is a common prefix, it must end in a slash.  In
	 * that case we let this loop run 1 into the prefix to see the
	 * same slash.
	 *
	 * If there is no common prefix, we cannot do this as it would
	 * underrun the input strings.
2357
	 */
2358
	pfx_adjust_for_slash = (pfx_length ? 1 : 0);
B
Brandon Williams 已提交
2359 2360 2361 2362 2363 2364 2365
	while (a + pfx_length - pfx_adjust_for_slash <= old_name &&
	       b + pfx_length - pfx_adjust_for_slash <= new_name &&
	       *old_name == *new_name) {
		if (*old_name == '/')
			sfx_length = len_a - (old_name - a);
		old_name--;
		new_name--;
J
Junio C Hamano 已提交
2366 2367 2368 2369 2370 2371 2372 2373
	}

	/*
	 * pfx{mid-a => mid-b}sfx
	 * {pfx-a => pfx-b}sfx
	 * pfx{sfx-a => sfx-b}
	 * name-a => name-b
	 */
2374 2375 2376 2377 2378 2379 2380
	a_midlen = len_a - pfx_length - sfx_length;
	b_midlen = len_b - pfx_length - sfx_length;
	if (a_midlen < 0)
		a_midlen = 0;
	if (b_midlen < 0)
		b_midlen = 0;

2381
	strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
J
Junio C Hamano 已提交
2382
	if (pfx_length + sfx_length) {
2383 2384
		strbuf_add(name, a, pfx_length);
		strbuf_addch(name, '{');
J
Junio C Hamano 已提交
2385
	}
2386 2387 2388
	strbuf_add(name, a + pfx_length, a_midlen);
	strbuf_addstr(name, " => ");
	strbuf_add(name, b + pfx_length, b_midlen);
2389
	if (pfx_length + sfx_length) {
2390 2391
		strbuf_addch(name, '}');
		strbuf_add(name, a + len_a - sfx_length, sfx_length);
J
Junio C Hamano 已提交
2392 2393 2394 2395 2396 2397 2398
	}
}

struct diffstat_t {
	int nr;
	int alloc;
	struct diffstat_file {
2399
		char *from_name;
J
Junio C Hamano 已提交
2400
		char *name;
2401
		char *print_name;
2402
		const char *comments;
J
Junio C Hamano 已提交
2403 2404 2405
		unsigned is_unmerged:1;
		unsigned is_binary:1;
		unsigned is_renamed:1;
2406
		unsigned is_interesting:1;
2407
		uintmax_t added, deleted;
J
Junio C Hamano 已提交
2408 2409 2410 2411 2412 2413 2414 2415
	} **files;
};

static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
					  const char *name_a,
					  const char *name_b)
{
	struct diffstat_file *x;
2416
	x = xcalloc(1, sizeof(*x));
D
Dmitry S. Dolzhenko 已提交
2417
	ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
J
Junio C Hamano 已提交
2418 2419
	diffstat->files[diffstat->nr++] = x;
	if (name_b) {
2420 2421
		x->from_name = xstrdup(name_a);
		x->name = xstrdup(name_b);
J
Junio C Hamano 已提交
2422 2423
		x->is_renamed = 1;
	}
2424 2425
	else {
		x->from_name = NULL;
2426
		x->name = xstrdup(name_a);
2427
	}
J
Junio C Hamano 已提交
2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441
	return x;
}

static void diffstat_consume(void *priv, char *line, unsigned long len)
{
	struct diffstat_t *diffstat = priv;
	struct diffstat_file *x = diffstat->files[diffstat->nr - 1];

	if (line[0] == '+')
		x->added++;
	else if (line[0] == '-')
		x->deleted++;
}

2442
const char mime_boundary_leader[] = "------------";
J
Junio C Hamano 已提交
2443

2444 2445
static int scale_linear(int it, int width, int max_change)
{
2446 2447
	if (!it)
		return 0;
2448
	/*
2449 2450 2451 2452
	 * make sure that at least one '-' or '+' is printed if
	 * there is any change to this path. The easiest way is to
	 * scale linearly as if the alloted width is one column shorter
	 * than it is, and then add 1 to the result.
2453
	 */
2454
	return 1 + (it * (width - 1) / max_change);
2455 2456
}

2457 2458
static void show_graph(struct strbuf *out, char ch, int cnt,
		       const char *set, const char *reset)
2459 2460 2461
{
	if (cnt <= 0)
		return;
2462 2463 2464
	strbuf_addstr(out, set);
	strbuf_addchars(out, ch, cnt);
	strbuf_addstr(out, reset);
2465 2466
}

2467 2468
static void fill_print_name(struct diffstat_file *file)
{
2469
	struct strbuf pname = STRBUF_INIT;
2470 2471 2472 2473

	if (file->print_name)
		return;

2474 2475 2476 2477 2478
	if (file->is_renamed)
		pprint_rename(&pname, file->from_name, file->name);
	else
		quote_c_style(file->name, &pname, NULL, 0);

2479 2480 2481
	if (file->comments)
		strbuf_addf(&pname, " (%s)", file->comments);

2482
	file->print_name = strbuf_detach(&pname, NULL);
2483 2484
}

2485 2486
static void print_stat_summary_inserts_deletes(struct diff_options *options,
		int files, int insertions, int deletions)
2487 2488 2489 2490 2491
{
	struct strbuf sb = STRBUF_INIT;

	if (!files) {
		assert(insertions == 0 && deletions == 0);
2492 2493 2494
		emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
				 NULL, 0, 0);
		return;
2495 2496 2497
	}

	strbuf_addf(&sb,
2498
		    (files == 1) ? " %d file changed" : " %d files changed",
2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
		    files);

	/*
	 * For binary diff, the caller may want to print "x files
	 * changed" with insertions == 0 && deletions == 0.
	 *
	 * Not omitting "0 insertions(+), 0 deletions(-)" in this case
	 * is probably less confusing (i.e skip over "2 files changed
	 * but nothing about added/removed lines? Is this a bug in Git?").
	 */
	if (insertions || deletions == 0) {
		strbuf_addf(&sb,
2511
			    (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
2512 2513 2514 2515 2516
			    insertions);
	}

	if (deletions || insertions == 0) {
		strbuf_addf(&sb,
2517
			    (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
2518 2519 2520
			    deletions);
	}
	strbuf_addch(&sb, '\n');
2521 2522
	emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
			 sb.buf, sb.len, 0);
2523
	strbuf_release(&sb);
2524 2525 2526 2527 2528 2529 2530 2531 2532 2533
}

void print_stat_summary(FILE *fp, int files,
			int insertions, int deletions)
{
	struct diff_options o;
	memset(&o, 0, sizeof(o));
	o.file = fp;

	print_stat_summary_inserts_deletes(&o, files, insertions, deletions);
2534 2535
}

2536
static void show_stats(struct diffstat_t *data, struct diff_options *options)
J
Junio C Hamano 已提交
2537
{
2538
	int i, len, add, del, adds = 0, dels = 0;
2539
	uintmax_t max_change = 0, max_len = 0;
2540 2541
	int total_files = data->nr, count;
	int width, name_width, graph_width, number_width = 0, bin_width = 0;
J
Johannes Schindelin 已提交
2542
	const char *reset, *add_c, *del_c;
2543
	int extra_shown = 0;
2544 2545
	const char *line_prefix = diff_line_prefix(options);
	struct strbuf out = STRBUF_INIT;
J
Junio C Hamano 已提交
2546 2547 2548 2549

	if (data->nr == 0)
		return;

2550
	count = options->stat_count ? options->stat_count : data->nr;
2551

2552 2553 2554
	reset = diff_get_color_opt(options, DIFF_RESET);
	add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
	del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
J
Junio C Hamano 已提交
2555

2556 2557 2558
	/*
	 * Find the longest filename and max number of changes
	 */
2559
	for (i = 0; (i < count) && (i < data->nr); i++) {
J
Junio C Hamano 已提交
2560
		struct diffstat_file *file = data->files[i];
2561
		uintmax_t change = file->added + file->deleted;
2562 2563

		if (!file->is_interesting && (change == 0)) {
2564
			count++; /* not shown == room for one more */
2565 2566
			continue;
		}
2567 2568
		fill_print_name(file);
		len = strlen(file->print_name);
J
Junio C Hamano 已提交
2569 2570 2571
		if (max_len < len)
			max_len = len;

2572 2573 2574
		if (file->is_unmerged) {
			/* "Unmerged" is 8 characters */
			bin_width = bin_width < 8 ? 8 : bin_width;
J
Junio C Hamano 已提交
2575
			continue;
2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586
		}
		if (file->is_binary) {
			/* "Bin XXX -> YYY bytes" */
			int w = 14 + decimal_width(file->added)
				+ decimal_width(file->deleted);
			bin_width = bin_width < w ? w : bin_width;
			/* Display change counts aligned with "Bin" */
			number_width = 3;
			continue;
		}

2587 2588
		if (max_change < change)
			max_change = change;
J
Junio C Hamano 已提交
2589
	}
2590
	count = i; /* where we can stop scanning in data->files[] */
J
Junio C Hamano 已提交
2591

2592 2593 2594
	/*
	 * We have width = stat_width or term_columns() columns total.
	 * We want a maximum of min(max_len, stat_name_width) for the name part.
2595
	 * We want a maximum of min(max_change, stat_graph_width) for the +- part.
2596 2597 2598 2599 2600 2601
	 * We also need 1 for " " and 4 + decimal_width(max_change)
	 * for " | NNNN " and one the empty column at the end, altogether
	 * 6 + decimal_width(max_change).
	 *
	 * If there's not enough space, we will use the smaller of
	 * stat_name_width (if set) and 5/8*width for the filename,
2602 2603
	 * and the rest for constant elements + graph part, but no more
	 * than stat_graph_width for the graph part.
2604 2605
	 * (5/8 gives 50 for filename and 30 for the constant parts + graph
	 * for the standard terminal size).
2606
	 *
2607 2608 2609 2610
	 * In other words: stat_width limits the maximum width, and
	 * stat_name_width fixes the maximum width of the filename,
	 * and is also used to divide available columns if there
	 * aren't enough.
2611 2612 2613 2614
	 *
	 * Binary files are displayed with "Bin XXX -> YYY bytes"
	 * instead of the change count and graph. This part is treated
	 * similarly to the graph part, except that it is not
2615
	 * "scaled". If total width is too small to accommodate the
2616 2617 2618
	 * guaranteed minimum width of the filename part and the
	 * separators and this message, this message will "overflow"
	 * making the line longer than the maximum width.
2619
	 */
2620 2621

	if (options->stat_width == -1)
2622
		width = term_columns() - strlen(line_prefix);
2623
	else
2624
		width = options->stat_width ? options->stat_width : 80;
2625 2626
	number_width = decimal_width(max_change) > number_width ?
		decimal_width(max_change) : number_width;
2627

2628 2629
	if (options->stat_graph_width == -1)
		options->stat_graph_width = diff_stat_graph_width;
2630

2631 2632 2633 2634 2635 2636 2637 2638 2639
	/*
	 * 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;

	/*
	 * First assign sizes that are wanted, ignoring available width.
2640 2641
	 * strlen("Bin XXX -> YYY bytes") == bin_width, and the part
	 * starting from "XXX" should fit in graph_width.
2642
	 */
2643 2644 2645 2646 2647
	graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4;
	if (options->stat_graph_width &&
	    options->stat_graph_width < graph_width)
		graph_width = options->stat_graph_width;

2648 2649 2650 2651 2652 2653 2654 2655
	name_width = (options->stat_name_width > 0 &&
		      options->stat_name_width < max_len) ?
		options->stat_name_width : max_len;

	/*
	 * Adjust adjustable widths not to exceed maximum width
	 */
	if (name_width + number_width + 6 + graph_width > width) {
2656
		if (graph_width > width * 3/8 - number_width - 6) {
2657
			graph_width = width * 3/8 - number_width - 6;
2658 2659 2660 2661
			if (graph_width < 6)
				graph_width = 6;
		}

2662 2663 2664
		if (options->stat_graph_width &&
		    graph_width > options->stat_graph_width)
			graph_width = options->stat_graph_width;
2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675
		if (name_width > width - number_width - 6 - graph_width)
			name_width = width - number_width - 6 - graph_width;
		else
			graph_width = width - number_width - 6 - name_width;
	}

	/*
	 * From here name_width is the width of the name area,
	 * and graph_width is the width of the graph area.
	 * max_change is used to scale graph properly.
	 */
2676
	for (i = 0; i < count; i++) {
T
Timo Hirvonen 已提交
2677
		const char *prefix = "";
2678 2679 2680 2681
		struct diffstat_file *file = data->files[i];
		char *name = file->print_name;
		uintmax_t added = file->added;
		uintmax_t deleted = file->deleted;
2682
		int name_len;
J
Junio C Hamano 已提交
2683

2684
		if (!file->is_interesting && (added + deleted == 0))
2685
			continue;
2686

J
Junio C Hamano 已提交
2687 2688 2689
		/*
		 * "scale" the filename
		 */
2690 2691 2692
		len = name_width;
		name_len = strlen(name);
		if (name_width < name_len) {
J
Junio C Hamano 已提交
2693 2694
			char *slash;
			prefix = "...";
2695 2696
			len -= 3;
			name += name_len - len;
J
Junio C Hamano 已提交
2697 2698 2699 2700 2701
			slash = strchr(name, '/');
			if (slash)
				name = slash;
		}

2702
		if (file->is_binary) {
2703 2704
			strbuf_addf(&out, " %s%-*s |", prefix, len, name);
			strbuf_addf(&out, " %*s", number_width, "Bin");
2705
			if (!added && !deleted) {
2706 2707 2708 2709
				strbuf_addch(&out, '\n');
				emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
						 out.buf, out.len, 0);
				strbuf_reset(&out);
2710 2711
				continue;
			}
2712
			strbuf_addf(&out, " %s%"PRIuMAX"%s",
2713
				del_c, deleted, reset);
2714 2715
			strbuf_addstr(&out, " -> ");
			strbuf_addf(&out, "%s%"PRIuMAX"%s",
2716
				add_c, added, reset);
2717 2718 2719 2720
			strbuf_addstr(&out, " bytes\n");
			emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
					 out.buf, out.len, 0);
			strbuf_reset(&out);
2721
			continue;
J
Junio C Hamano 已提交
2722
		}
2723
		else if (file->is_unmerged) {
2724 2725 2726 2727 2728
			strbuf_addf(&out, " %s%-*s |", prefix, len, name);
			strbuf_addstr(&out, " Unmerged\n");
			emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
					 out.buf, out.len, 0);
			strbuf_reset(&out);
2729
			continue;
J
Junio C Hamano 已提交
2730 2731
		}

2732 2733 2734
		/*
		 * scale the add/delete
		 */
J
Junio C Hamano 已提交
2735 2736 2737
		add = added;
		del = deleted;

2738
		if (graph_width <= max_change) {
2739
			int total = scale_linear(add + del, graph_width, max_change);
2740 2741 2742 2743
			if (total < 2 && add && del)
				/* width >= 2 due to the sanity check */
				total = 2;
			if (add < del) {
2744
				add = scale_linear(add, graph_width, max_change);
2745 2746
				del = total - add;
			} else {
2747
				del = scale_linear(del, graph_width, max_change);
2748 2749
				add = total - del;
			}
J
Junio C Hamano 已提交
2750
		}
2751 2752
		strbuf_addf(&out, " %s%-*s |", prefix, len, name);
		strbuf_addf(&out, " %*"PRIuMAX"%s",
2753 2754
			number_width, added + deleted,
			added + deleted ? " " : "");
2755 2756 2757 2758 2759 2760
		show_graph(&out, '+', add, add_c, reset);
		show_graph(&out, '-', del, del_c, reset);
		strbuf_addch(&out, '\n');
		emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
				 out.buf, out.len, 0);
		strbuf_reset(&out);
2761
	}
2762 2763

	for (i = 0; i < data->nr; i++) {
2764 2765 2766
		struct diffstat_file *file = data->files[i];
		uintmax_t added = file->added;
		uintmax_t deleted = file->deleted;
2767 2768 2769

		if (file->is_unmerged ||
		    (!file->is_interesting && (added + deleted == 0))) {
2770 2771 2772
			total_files--;
			continue;
		}
2773

2774
		if (!file->is_binary) {
2775 2776 2777 2778 2779
			adds += added;
			dels += deleted;
		}
		if (i < count)
			continue;
2780
		if (!extra_shown)
2781 2782 2783
			emit_diff_symbol(options,
					 DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
					 NULL, 0, 0);
2784
		extra_shown = 1;
2785
	}
2786 2787

	print_stat_summary_inserts_deletes(options, total_files, adds, dels);
2788
	strbuf_release(&out);
J
Junio C Hamano 已提交
2789 2790
}

F
Felipe Contreras 已提交
2791
static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
2792 2793 2794 2795 2796 2797 2798
{
	int i, adds = 0, dels = 0, total_files = data->nr;

	if (data->nr == 0)
		return;

	for (i = 0; i < data->nr; i++) {
2799
		int added = data->files[i]->added;
2800
		int deleted = data->files[i]->deleted;
2801

2802 2803
		if (data->files[i]->is_unmerged ||
		    (!data->files[i]->is_interesting && (added + deleted == 0))) {
2804
			total_files--;
2805
		} else if (!data->files[i]->is_binary) { /* don't count bytes */
2806 2807
			adds += added;
			dels += deleted;
2808 2809
		}
	}
2810
	print_stat_summary_inserts_deletes(options, total_files, adds, dels);
2811 2812
}

2813
static void show_numstat(struct diffstat_t *data, struct diff_options *options)
J
Junio C Hamano 已提交
2814 2815 2816
{
	int i;

2817 2818 2819
	if (data->nr == 0)
		return;

J
Junio C Hamano 已提交
2820 2821 2822
	for (i = 0; i < data->nr; i++) {
		struct diffstat_file *file = data->files[i];

2823
		fprintf(options->file, "%s", diff_line_prefix(options));
2824

2825
		if (file->is_binary)
2826
			fprintf(options->file, "-\t-\t");
2827
		else
2828
			fprintf(options->file,
2829 2830
				"%"PRIuMAX"\t%"PRIuMAX"\t",
				file->added, file->deleted);
2831 2832 2833
		if (options->line_termination) {
			fill_print_name(file);
			if (!file->is_renamed)
2834
				write_name_quoted(file->name, options->file,
2835 2836
						  options->line_termination);
			else {
2837 2838
				fputs(file->print_name, options->file);
				putc(options->line_termination, options->file);
2839
			}
2840
		} else {
2841
			if (file->is_renamed) {
2842 2843
				putc('\0', options->file);
				write_name_quoted(file->from_name, options->file, '\0');
2844
			}
2845
			write_name_quoted(file->name, options->file, '\0');
2846
		}
J
Junio C Hamano 已提交
2847 2848 2849
	}
}

2850 2851 2852
struct dirstat_file {
	const char *name;
	unsigned long changed;
2853 2854
};

2855 2856
struct dirstat_dir {
	struct dirstat_file *files;
2857
	int alloc, nr, permille, cumulative;
2858 2859
};

2860 2861
static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
		unsigned long changed, const char *base, int baselen)
2862
{
B
Brandon Williams 已提交
2863
	unsigned long sum_changes = 0;
2864
	unsigned int sources = 0;
2865
	const char *line_prefix = diff_line_prefix(opt);
2866 2867

	while (dir->nr) {
2868
		struct dirstat_file *f = dir->files;
2869
		int namelen = strlen(f->name);
B
Brandon Williams 已提交
2870
		unsigned long changes;
2871 2872 2873 2874 2875 2876 2877 2878 2879
		char *slash;

		if (namelen < baselen)
			break;
		if (memcmp(f->name, base, baselen))
			break;
		slash = strchr(f->name + baselen, '/');
		if (slash) {
			int newbaselen = slash + 1 - f->name;
B
Brandon Williams 已提交
2880
			changes = gather_dirstat(opt, dir, changed, f->name, newbaselen);
2881 2882
			sources++;
		} else {
B
Brandon Williams 已提交
2883
			changes = f->changed;
2884 2885 2886 2887
			dir->files++;
			dir->nr--;
			sources += 2;
		}
B
Brandon Williams 已提交
2888
		sum_changes += changes;
2889 2890 2891 2892 2893 2894 2895 2896 2897
	}

	/*
	 * We don't report dirstat's for
	 *  - the top level
	 *  - or cases where everything came from a single directory
	 *    under this directory (sources == 1).
	 */
	if (baselen && sources != 1) {
B
Brandon Williams 已提交
2898 2899
		if (sum_changes) {
			int permille = sum_changes * 1000 / changed;
2900
			if (permille >= dir->permille) {
2901
				fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
2902
					permille / 10, permille % 10, baselen, base);
2903 2904 2905 2906 2907
				if (!dir->cumulative)
					return 0;
			}
		}
	}
B
Brandon Williams 已提交
2908
	return sum_changes;
2909 2910
}

2911 2912 2913 2914 2915 2916 2917
static int dirstat_compare(const void *_a, const void *_b)
{
	const struct dirstat_file *a = _a;
	const struct dirstat_file *b = _b;
	return strcmp(a->name, b->name);
}

2918
static void show_dirstat(struct diff_options *options)
2919 2920 2921
{
	int i;
	unsigned long changed;
2922 2923 2924 2925 2926 2927
	struct dirstat_dir dir;
	struct diff_queue_struct *q = &diff_queued_diff;

	dir.files = NULL;
	dir.alloc = 0;
	dir.nr = 0;
2928
	dir.permille = options->dirstat_permille;
2929
	dir.cumulative = options->flags.dirstat_cumulative;
2930 2931

	changed = 0;
2932 2933 2934 2935
	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];
		const char *name;
		unsigned long copied, added, damage;
2936
		int content_changed;
2937

2938
		name = p->two->path ? p->two->path : p->one->path;
2939

2940
		if (p->one->oid_valid && p->two->oid_valid)
2941
			content_changed = oidcmp(&p->one->oid, &p->two->oid);
2942 2943 2944
		else
			content_changed = 1;

2945 2946 2947 2948 2949 2950 2951 2952 2953 2954
		if (!content_changed) {
			/*
			 * The SHA1 has not changed, so pre-/post-content is
			 * identical. We can therefore skip looking at the
			 * file contents altogether.
			 */
			damage = 0;
			goto found_damage;
		}

2955
		if (options->flags.dirstat_by_file) {
2956 2957 2958 2959 2960 2961 2962
			/*
			 * In --dirstat-by-file mode, we don't really need to
			 * look at the actual file contents at all.
			 * The fact that the SHA1 changed is enough for us to
			 * add this file to the list of results
			 * (with each file contributing equal damage).
			 */
2963
			damage = 1;
2964 2965
			goto found_damage;
		}
2966 2967 2968 2969

		if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
			diff_populate_filespec(p->one, 0);
			diff_populate_filespec(p->two, 0);
2970
			diffcore_count_changes(p->one, p->two, NULL, NULL,
2971 2972 2973 2974
					       &copied, &added);
			diff_free_filespec_data(p->one);
			diff_free_filespec_data(p->two);
		} else if (DIFF_FILE_VALID(p->one)) {
2975
			diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
2976 2977 2978
			copied = added = 0;
			diff_free_filespec_data(p->one);
		} else if (DIFF_FILE_VALID(p->two)) {
2979
			diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
2980 2981 2982 2983
			copied = 0;
			added = p->two->size;
			diff_free_filespec_data(p->two);
		} else
2984
			continue;
2985 2986 2987 2988

		/*
		 * Original minus copied is the removed material,
		 * added is the new material.  They are both damages
2989
		 * made to the preimage.
2990 2991 2992 2993 2994
		 * If the resulting damage is zero, we know that
		 * diffcore_count_changes() considers the two entries to
		 * be identical, but since content_changed is true, we
		 * know that there must have been _some_ kind of change,
		 * so we force all entries to have damage > 0.
2995 2996
		 */
		damage = (p->one->size - copied) + added;
2997
		if (!damage)
2998
			damage = 1;
2999

3000
found_damage:
3001 3002 3003 3004 3005
		ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
		dir.files[dir.nr].name = name;
		dir.files[dir.nr].changed = damage;
		changed += damage;
		dir.nr++;
3006 3007 3008 3009 3010 3011 3012
	}

	/* This can happen even with many files, if everything was renames */
	if (!changed)
		return;

	/* Show all directories with more than x% of the changes */
R
René Scharfe 已提交
3013
	QSORT(dir.files, dir.nr, dirstat_compare);
3014
	gather_dirstat(options, &dir, changed, "", 0);
3015 3016
}

3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029
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;
3030
	dir.cumulative = options->flags.dirstat_cumulative;
3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043

	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...
			 */
R
René Scharfe 已提交
3044
			damage = DIV_ROUND_UP(damage, 64);
3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056
		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 */
R
René Scharfe 已提交
3057
	QSORT(dir.files, dir.nr, dirstat_compare);
3058 3059 3060
	gather_dirstat(options, &dir, changed, "", 0);
}

3061 3062 3063 3064 3065
static void free_diffstat_info(struct diffstat_t *diffstat)
{
	int i;
	for (i = 0; i < diffstat->nr; i++) {
		struct diffstat_file *f = diffstat->files[i];
3066
		free(f->print_name);
3067 3068 3069 3070 3071 3072 3073
		free(f->name);
		free(f->from_name);
		free(f);
	}
	free(diffstat->files);
}

3074 3075
struct checkdiff_t {
	const char *filename;
3076
	int lineno;
3077
	int conflict_marker_size;
3078
	struct diff_options *o;
3079
	unsigned ws_rule;
3080
	unsigned status;
3081 3082
};

3083
static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
3084 3085 3086 3087
{
	char firstchar;
	int cnt;

3088
	if (len < marker_size + 1)
3089 3090 3091
		return 0;
	firstchar = line[0];
	switch (firstchar) {
3092
	case '=': case '>': case '<': case '|':
3093 3094 3095 3096
		break;
	default:
		return 0;
	}
3097
	for (cnt = 1; cnt < marker_size; cnt++)
3098 3099
		if (line[cnt] != firstchar)
			return 0;
3100 3101
	/* line[1] thru line[marker_size-1] are same as firstchar */
	if (len < marker_size + 1 || !isspace(line[marker_size]))
3102 3103 3104 3105
		return 0;
	return 1;
}

3106 3107 3108
static void checkdiff_consume(void *priv, char *line, unsigned long len)
{
	struct checkdiff_t *data = priv;
3109
	int marker_size = data->conflict_marker_size;
3110 3111 3112
	const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE);
	const char *reset = diff_get_color(data->o->use_color, DIFF_RESET);
	const char *set = diff_get_color(data->o->use_color, DIFF_FILE_NEW);
W
Wincent Colaiuta 已提交
3113
	char *err;
3114
	const char *line_prefix;
3115 3116

	assert(data->o);
3117
	line_prefix = diff_line_prefix(data->o);
3118 3119

	if (line[0] == '+') {
3120
		unsigned bad;
3121
		data->lineno++;
3122
		if (is_conflict_marker(line + 1, marker_size, len - 1)) {
3123 3124
			data->status |= 1;
			fprintf(data->o->file,
3125 3126
				"%s%s:%d: leftover conflict marker\n",
				line_prefix, data->filename, data->lineno);
3127
		}
3128
		bad = ws_check(line + 1, len - 1, data->ws_rule);
3129
		if (!bad)
W
Wincent Colaiuta 已提交
3130
			return;
3131 3132
		data->status |= bad;
		err = whitespace_error_string(bad);
3133 3134
		fprintf(data->o->file, "%s%s:%d: %s.\n",
			line_prefix, data->filename, data->lineno, err);
W
Wincent Colaiuta 已提交
3135
		free(err);
3136
		emit_line(data->o, set, reset, line, 1);
3137
		ws_check_emit(line + 1, len - 1, data->ws_rule,
3138
			      data->o->file, set, reset, ws);
3139
	} else if (line[0] == ' ') {
3140
		data->lineno++;
3141
	} else if (line[0] == '@') {
3142 3143
		char *plus = strchr(line, '+');
		if (plus)
3144
			data->lineno = strtol(plus, NULL, 10) - 1;
3145 3146 3147 3148 3149
		else
			die("invalid diff");
	}
}

J
Junio C Hamano 已提交
3150 3151 3152
static unsigned char *deflate_it(char *data,
				 unsigned long size,
				 unsigned long *result_size)
J
Junio C Hamano 已提交
3153
{
J
Junio C Hamano 已提交
3154 3155
	int bound;
	unsigned char *deflated;
3156
	git_zstream stream;
J
Junio C Hamano 已提交
3157

3158
	git_deflate_init(&stream, zlib_compression_level);
J
Junio C Hamano 已提交
3159
	bound = git_deflate_bound(&stream, size);
J
Junio C Hamano 已提交
3160 3161 3162 3163 3164 3165
	deflated = xmalloc(bound);
	stream.next_out = deflated;
	stream.avail_out = bound;

	stream.next_in = (unsigned char *)data;
	stream.avail_in = size;
3166
	while (git_deflate(&stream, Z_FINISH) == Z_OK)
J
Junio C Hamano 已提交
3167
		; /* nothing */
3168
	git_deflate_end(&stream);
J
Junio C Hamano 已提交
3169 3170
	*result_size = stream.total_out;
	return deflated;
J
Junio C Hamano 已提交
3171 3172
}

3173 3174
static void emit_binary_diff_body(struct diff_options *o,
				  mmfile_t *one, mmfile_t *two)
J
Junio C Hamano 已提交
3175
{
J
Junio C Hamano 已提交
3176 3177 3178 3179 3180 3181 3182 3183
	void *cp;
	void *delta;
	void *deflated;
	void *data;
	unsigned long orig_size;
	unsigned long delta_size;
	unsigned long deflate_size;
	unsigned long data_size;
J
Junio C Hamano 已提交
3184

J
Junio C Hamano 已提交
3185 3186
	/* We could do deflated delta, or we could do just deflated two,
	 * whichever is smaller.
J
Junio C Hamano 已提交
3187
	 */
J
Junio C Hamano 已提交
3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198
	delta = NULL;
	deflated = deflate_it(two->ptr, two->size, &deflate_size);
	if (one->size && two->size) {
		delta = diff_delta(one->ptr, one->size,
				   two->ptr, two->size,
				   &delta_size, deflate_size);
		if (delta) {
			void *to_free = delta;
			orig_size = delta_size;
			delta = deflate_it(delta, delta_size, &delta_size);
			free(to_free);
J
Junio C Hamano 已提交
3199 3200 3201
		}
	}

J
Junio C Hamano 已提交
3202
	if (delta && delta_size < deflate_size) {
3203 3204 3205 3206
		char *s = xstrfmt("%lu", orig_size);
		emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
				 s, strlen(s), 0);
		free(s);
J
Junio C Hamano 已提交
3207 3208 3209
		free(deflated);
		data = delta;
		data_size = delta_size;
3210 3211 3212 3213 3214
	} else {
		char *s = xstrfmt("%lu", two->size);
		emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
				 s, strlen(s), 0);
		free(s);
J
Junio C Hamano 已提交
3215 3216 3217 3218
		free(delta);
		data = deflated;
		data_size = deflate_size;
	}
J
Junio C Hamano 已提交
3219

J
Junio C Hamano 已提交
3220 3221 3222
	/* emit data encoded in base85 */
	cp = data;
	while (data_size) {
3223
		int len;
J
Junio C Hamano 已提交
3224
		int bytes = (52 < data_size) ? 52 : data_size;
3225
		char line[71];
J
Junio C Hamano 已提交
3226
		data_size -= bytes;
J
Junio C Hamano 已提交
3227 3228 3229 3230 3231
		if (bytes <= 26)
			line[0] = bytes + 'A' - 1;
		else
			line[0] = bytes - 26 + 'a' - 1;
		encode_85(line + 1, cp, bytes);
3232
		cp = (char *) cp + bytes;
3233 3234 3235 3236 3237 3238 3239

		len = strlen(line);
		line[len++] = '\n';
		line[len] = '\0';

		emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_BODY,
				 line, len, 0);
J
Junio C Hamano 已提交
3240
	}
3241
	emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_FOOTER, NULL, 0, 0);
J
Junio C Hamano 已提交
3242
	free(data);
J
Junio C Hamano 已提交
3243 3244
}

3245 3246
static void emit_binary_diff(struct diff_options *o,
			     mmfile_t *one, mmfile_t *two)
3247
{
3248 3249 3250
	emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER, NULL, 0, 0);
	emit_binary_diff_body(o, one, two);
	emit_binary_diff_body(o, two, one);
3251 3252
}

3253 3254
int diff_filespec_is_binary(struct diff_filespec *one)
{
J
Jeff King 已提交
3255 3256 3257 3258 3259 3260
	if (one->is_binary == -1) {
		diff_filespec_load_driver(one);
		if (one->driver->binary != -1)
			one->is_binary = one->driver->binary;
		else {
			if (!one->data && DIFF_FILE_VALID(one))
3261 3262
				diff_populate_filespec(one, CHECK_BINARY);
			if (one->is_binary == -1 && one->data)
J
Jeff King 已提交
3263 3264 3265 3266 3267 3268
				one->is_binary = buffer_is_binary(one->data,
						one->size);
			if (one->is_binary == -1)
				one->is_binary = 0;
		}
	}
3269
	return one->is_binary;
J
Junio C Hamano 已提交
3270 3271
}

3272
static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
3273
{
J
Jeff King 已提交
3274 3275
	diff_filespec_load_driver(one);
	return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
3276 3277
}

3278 3279 3280 3281 3282 3283 3284 3285
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
{
	if (!options->a_prefix)
		options->a_prefix = a;
	if (!options->b_prefix)
		options->b_prefix = b;
}

A
Axel Bonnet 已提交
3286
struct userdiff_driver *get_textconv(struct diff_filespec *one)
J
Jeff King 已提交
3287 3288 3289
{
	if (!DIFF_FILE_VALID(one))
		return NULL;
3290

J
Jeff King 已提交
3291
	diff_filespec_load_driver(one);
3292
	return userdiff_get_textconv(one->driver);
J
Jeff King 已提交
3293 3294
}

J
Junio C Hamano 已提交
3295 3296 3297 3298 3299
static void builtin_diff(const char *name_a,
			 const char *name_b,
			 struct diff_filespec *one,
			 struct diff_filespec *two,
			 const char *xfrm_msg,
3300
			 int must_show_header,
J
Junio C Hamano 已提交
3301
			 struct diff_options *o,
J
Junio C Hamano 已提交
3302 3303 3304 3305 3306
			 int complete_rewrite)
{
	mmfile_t mf1, mf2;
	const char *lbl[2];
	char *a_one, *b_two;
J
Jeff King 已提交
3307
	const char *meta = diff_get_color_opt(o, DIFF_METAINFO);
3308
	const char *reset = diff_get_color_opt(o, DIFF_RESET);
3309
	const char *a_prefix, *b_prefix;
J
Jeff King 已提交
3310 3311
	struct userdiff_driver *textconv_one = NULL;
	struct userdiff_driver *textconv_two = NULL;
3312
	struct strbuf header = STRBUF_INIT;
3313
	const char *line_prefix = diff_line_prefix(o);
3314

3315
	diff_set_mnemonic_prefix(o, "a/", "b/");
3316
	if (o->flags.reverse_diff) {
3317 3318 3319 3320 3321 3322 3323
		a_prefix = o->b_prefix;
		b_prefix = o->a_prefix;
	} else {
		a_prefix = o->a_prefix;
		b_prefix = o->b_prefix;
	}

3324 3325 3326
	if (o->submodule_format == DIFF_SUBMODULE_LOG &&
	    (!one->mode || S_ISGITLINK(one->mode)) &&
	    (!two->mode || S_ISGITLINK(two->mode))) {
3327
		show_submodule_summary(o, one->path ? one->path : two->path,
3328
				&one->oid, &two->oid,
3329
				two->dirty_submodule);
3330
		return;
3331 3332 3333
	} else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
		   (!one->mode || S_ISGITLINK(one->mode)) &&
		   (!two->mode || S_ISGITLINK(two->mode))) {
3334
		show_submodule_inline_diff(o, one->path ? one->path : two->path,
3335
				&one->oid, &two->oid,
3336
				two->dirty_submodule);
3337
		return;
3338 3339
	}

3340
	if (o->flags.allow_textconv) {
3341 3342 3343 3344
		textconv_one = get_textconv(one);
		textconv_two = get_textconv(two);
	}

3345 3346 3347 3348
	/* Never use a non-valid filename anywhere if at all possible */
	name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
	name_b = DIFF_FILE_VALID(two) ? name_b : name_a;

3349 3350
	a_one = quote_two(a_prefix, name_a + (*name_a == '/'));
	b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
J
Junio C Hamano 已提交
3351 3352
	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
J
Jeff King 已提交
3353
	strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
J
Junio C Hamano 已提交
3354 3355
	if (lbl[0][0] == '/') {
		/* /dev/null */
J
Jeff King 已提交
3356
		strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
3357 3358
		if (xfrm_msg)
			strbuf_addstr(&header, xfrm_msg);
3359
		must_show_header = 1;
J
Junio C Hamano 已提交
3360 3361
	}
	else if (lbl[1][0] == '/') {
J
Jeff King 已提交
3362
		strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
3363 3364
		if (xfrm_msg)
			strbuf_addstr(&header, xfrm_msg);
3365
		must_show_header = 1;
J
Junio C Hamano 已提交
3366 3367 3368
	}
	else {
		if (one->mode != two->mode) {
J
Jeff King 已提交
3369 3370
			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);
3371
			must_show_header = 1;
J
Johannes Schindelin 已提交
3372
		}
3373 3374
		if (xfrm_msg)
			strbuf_addstr(&header, xfrm_msg);
3375

J
Junio C Hamano 已提交
3376 3377 3378 3379 3380 3381
		/*
		 * we do not run diff between different kind
		 * of objects.
		 */
		if ((one->mode ^ two->mode) & S_IFMT)
			goto free_ab_and_return;
3382
		if (complete_rewrite &&
3383 3384
		    (textconv_one || !diff_filespec_is_binary(one)) &&
		    (textconv_two || !diff_filespec_is_binary(two))) {
3385 3386
			emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
					 header.buf, header.len, 0);
3387
			strbuf_reset(&header);
3388 3389
			emit_rewrite_diff(name_a, name_b, one, two,
						textconv_one, textconv_two, o);
3390
			o->found_changes = 1;
J
Junio C Hamano 已提交
3391 3392 3393 3394
			goto free_ab_and_return;
		}
	}

3395
	if (o->irreversible_delete && lbl[1][0] == '/') {
3396 3397
		emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf,
				 header.len, 0);
3398 3399
		strbuf_reset(&header);
		goto free_ab_and_return;
3400
	} else if (!o->flags.text &&
3401 3402
	    ( (!textconv_one && diff_filespec_is_binary(one)) ||
	      (!textconv_two && diff_filespec_is_binary(two)) )) {
3403
		struct strbuf sb = STRBUF_INIT;
3404 3405
		if (!one->data && !two->data &&
		    S_ISREG(one->mode) && S_ISREG(two->mode) &&
3406
		    !o->flags.binary) {
J
Jeff King 已提交
3407
			if (oideq(&one->oid, &two->oid)) {
3408
				if (must_show_header)
3409 3410 3411
					emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
							 header.buf, header.len,
							 0);
3412 3413
				goto free_ab_and_return;
			}
3414 3415
			emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
					 header.buf, header.len, 0);
3416 3417 3418 3419 3420
			strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
				    diff_line_prefix(o), lbl[0], lbl[1]);
			emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
					 sb.buf, sb.len, 0);
			strbuf_release(&sb);
3421 3422
			goto free_ab_and_return;
		}
3423 3424
		if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
			die("unable to read files to diff");
J
Junio C Hamano 已提交
3425 3426
		/* Quite common confusing case */
		if (mf1.size == mf2.size &&
3427 3428
		    !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
			if (must_show_header)
3429 3430
				emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
						 header.buf, header.len, 0);
J
Junio C Hamano 已提交
3431
			goto free_ab_and_return;
3432
		}
3433
		emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
3434
		strbuf_reset(&header);
3435
		if (o->flags.binary)
3436
			emit_binary_diff(o, &mf1, &mf2);
3437 3438 3439 3440 3441 3442 3443
		else {
			strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
				    diff_line_prefix(o), lbl[0], lbl[1]);
			emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
					 sb.buf, sb.len, 0);
			strbuf_release(&sb);
		}
3444
		o->found_changes = 1;
3445
	} else {
J
Junio C Hamano 已提交
3446 3447
		/* Crazy xdl interfaces.. */
		const char *diffopts = getenv("GIT_DIFF_OPTS");
3448
		const char *v;
J
Junio C Hamano 已提交
3449 3450 3451
		xpparam_t xpp;
		xdemitconf_t xecfg;
		struct emit_callback ecbdata;
3452
		const struct userdiff_funcname *pe;
3453

3454
		if (must_show_header) {
3455 3456
			emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
					 header.buf, header.len, 0);
3457 3458 3459
			strbuf_reset(&header);
		}

3460 3461
		mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
		mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
J
Jeff King 已提交
3462

3463 3464 3465
		pe = diff_funcname_pattern(one);
		if (!pe)
			pe = diff_funcname_pattern(two);
J
Junio C Hamano 已提交
3466

B
Brian Downing 已提交
3467
		memset(&xpp, 0, sizeof(xpp));
3468
		memset(&xecfg, 0, sizeof(xecfg));
J
Johannes Schindelin 已提交
3469
		memset(&ecbdata, 0, sizeof(ecbdata));
3470 3471
		if (o->flags.suppress_diff_headers)
			lbl[0] = NULL;
J
Junio C Hamano 已提交
3472
		ecbdata.label_path = lbl;
3473
		ecbdata.color_diff = want_color(o->use_color);
3474
		ecbdata.ws_rule = whitespace_rule(name_b);
3475
		if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
3476
			check_blank_at_eof(&mf1, &mf2, &ecbdata);
3477
		ecbdata.opt = o;
3478 3479
		if (header.len && !o->flags.suppress_diff_headers)
			ecbdata.header = &header;
R
René Scharfe 已提交
3480
		xpp.flags = o->xdl_opts;
J
Jonathan Tan 已提交
3481 3482
		xpp.anchors = o->anchors;
		xpp.anchors_nr = o->anchors_nr;
3483
		xecfg.ctxlen = o->context;
3484
		xecfg.interhunkctxlen = o->interhunkcontext;
J
Junio C Hamano 已提交
3485
		xecfg.flags = XDL_EMIT_FUNCNAMES;
3486
		if (o->flags.funccontext)
3487
			xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
3488
		if (pe)
3489
			xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
J
Junio C Hamano 已提交
3490 3491
		if (!diffopts)
			;
3492 3493 3494 3495
		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);
3496 3497
		if (o->word_diff)
			init_diff_words_data(&ecbdata, o, one, two);
J
Jeff King 已提交
3498 3499 3500
		if (xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
				  &xpp, &xecfg))
			die("unable to generate diff for %s", one->path);
3501
		if (o->word_diff)
3502
			free_diff_words_data(&ecbdata);
J
Jeff King 已提交
3503 3504 3505 3506
		if (textconv_one)
			free(mf1.ptr);
		if (textconv_two)
			free(mf2.ptr);
3507
		xdiff_clear_find_func(&xecfg);
J
Junio C Hamano 已提交
3508 3509 3510
	}

 free_ab_and_return:
3511
	strbuf_release(&header);
3512 3513
	diff_free_filespec_data(one);
	diff_free_filespec_data(two);
J
Junio C Hamano 已提交
3514 3515 3516 3517 3518
	free(a_one);
	free(b_two);
	return;
}

3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544
static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
{
	if (!is_renamed) {
		if (p->status == DIFF_STATUS_ADDED) {
			if (S_ISLNK(p->two->mode))
				return "new +l";
			else if ((p->two->mode & 0777) == 0755)
				return "new +x";
			else
				return "new";
		} else if (p->status == DIFF_STATUS_DELETED)
			return "gone";
	}
	if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode))
		return "mode -l";
	else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode))
		return "mode +l";
	else if ((p->one->mode & 0777) == 0644 &&
		 (p->two->mode & 0777) == 0755)
		return "mode +x";
	else if ((p->one->mode & 0777) == 0755 &&
		 (p->two->mode & 0777) == 0644)
		return "mode -x";
	return NULL;
}

J
Junio C Hamano 已提交
3545 3546 3547
static void builtin_diffstat(const char *name_a, const char *name_b,
			     struct diff_filespec *one,
			     struct diff_filespec *two,
3548
			     struct diffstat_t *diffstat,
3549
			     struct diff_options *o,
3550
			     struct diff_filepair *p)
J
Junio C Hamano 已提交
3551 3552 3553
{
	mmfile_t mf1, mf2;
	struct diffstat_file *data;
3554
	int same_contents;
3555 3556 3557 3558 3559 3560
	int complete_rewrite = 0;

	if (!DIFF_PAIR_UNMERGED(p)) {
		if (p->status == DIFF_STATUS_MODIFIED && p->score)
			complete_rewrite = 1;
	}
J
Junio C Hamano 已提交
3561 3562

	data = diffstat_add(diffstat, name_a, name_b);
3563
	data->is_interesting = p->status != DIFF_STATUS_UNKNOWN;
3564 3565
	if (o->flags.stat_with_summary)
		data->comments = get_compact_summary(p, data->is_renamed);
J
Junio C Hamano 已提交
3566 3567 3568 3569 3570

	if (!one || !two) {
		data->is_unmerged = 1;
		return;
	}
3571

J
Jeff King 已提交
3572
	same_contents = oideq(&one->oid, &two->oid);
3573

3574 3575
	if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
		data->is_binary = 1;
3576
		if (same_contents) {
3577 3578 3579 3580 3581 3582
			data->added = 0;
			data->deleted = 0;
		} else {
			data->added = diff_filespec_size(two);
			data->deleted = diff_filespec_size(one);
		}
3583 3584 3585
	}

	else if (complete_rewrite) {
3586 3587 3588 3589 3590
		diff_populate_filespec(one, 0);
		diff_populate_filespec(two, 0);
		data->deleted = count_lines(one->data, one->size);
		data->added = count_lines(two->data, two->size);
	}
J
Junio C Hamano 已提交
3591

3592
	else if (!same_contents) {
J
Junio C Hamano 已提交
3593 3594 3595 3596
		/* Crazy xdl interfaces.. */
		xpparam_t xpp;
		xdemitconf_t xecfg;

3597 3598 3599
		if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
			die("unable to read files to diff");

B
Brian Downing 已提交
3600
		memset(&xpp, 0, sizeof(xpp));
3601
		memset(&xecfg, 0, sizeof(xecfg));
R
René Scharfe 已提交
3602
		xpp.flags = o->xdl_opts;
J
Jonathan Tan 已提交
3603 3604
		xpp.anchors = o->anchors;
		xpp.anchors_nr = o->anchors_nr;
3605 3606
		xecfg.ctxlen = o->context;
		xecfg.interhunkctxlen = o->interhunkcontext;
J
Jeff King 已提交
3607 3608 3609
		if (xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
				  &xpp, &xecfg))
			die("unable to generate diffstat for %s", one->path);
J
Junio C Hamano 已提交
3610
	}
3611 3612 3613

	diff_free_filespec_data(one);
	diff_free_filespec_data(two);
J
Junio C Hamano 已提交
3614 3615
}

3616
static void builtin_checkdiff(const char *name_a, const char *name_b,
3617
			      const char *attr_path,
3618 3619 3620
			      struct diff_filespec *one,
			      struct diff_filespec *two,
			      struct diff_options *o)
3621 3622 3623 3624 3625 3626 3627 3628 3629 3630
{
	mmfile_t mf1, mf2;
	struct checkdiff_t data;

	if (!two)
		return;

	memset(&data, 0, sizeof(data));
	data.filename = name_b ? name_b : name_a;
	data.lineno = 0;
3631
	data.o = o;
3632
	data.ws_rule = whitespace_rule(attr_path);
3633
	data.conflict_marker_size = ll_merge_marker_size(attr_path);
3634 3635 3636 3637

	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
		die("unable to read files to diff");

3638 3639 3640 3641 3642 3643
	/*
	 * All the other codepaths check both sides, but not checking
	 * the "old" side here is deliberate.  We are checking the newly
	 * introduced changes, and as long as the "new" side is text, we
	 * can and should check what it introduces.
	 */
3644
	if (diff_filespec_is_binary(two))
3645
		goto free_and_return;
3646 3647 3648 3649 3650
	else {
		/* Crazy xdl interfaces.. */
		xpparam_t xpp;
		xdemitconf_t xecfg;

B
Brian Downing 已提交
3651
		memset(&xpp, 0, sizeof(xpp));
3652
		memset(&xecfg, 0, sizeof(xecfg));
3653
		xecfg.ctxlen = 1; /* at least one context line */
R
René Scharfe 已提交
3654
		xpp.flags = 0;
J
Jeff King 已提交
3655 3656 3657
		if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
				  &xpp, &xecfg))
			die("unable to generate checkdiff for %s", one->path);
3658

3659
		if (data.ws_rule & WS_BLANK_AT_EOF) {
3660 3661 3662 3663 3664
			struct emit_callback ecbdata;
			int blank_at_eof;

			ecbdata.ws_rule = data.ws_rule;
			check_blank_at_eof(&mf1, &mf2, &ecbdata);
3665
			blank_at_eof = ecbdata.blank_at_eof_in_postimage;
3666

3667 3668 3669 3670 3671 3672 3673 3674
			if (blank_at_eof) {
				static char *err;
				if (!err)
					err = whitespace_error_string(WS_BLANK_AT_EOF);
				fprintf(o->file, "%s:%d: %s.\n",
					data.filename, blank_at_eof, err);
				data.status = 1; /* report errors */
			}
3675
		}
3676
	}
3677 3678 3679
 free_and_return:
	diff_free_filespec_data(one);
	diff_free_filespec_data(two);
3680
	if (data.status)
3681
		o->flags.check_failed = 1;
3682 3683
}

J
Junio C Hamano 已提交
3684 3685
struct diff_filespec *alloc_filespec(const char *path)
{
3686
	struct diff_filespec *spec;
J
Junio C Hamano 已提交
3687

3688
	FLEXPTR_ALLOC_STR(spec, path, path);
3689
	spec->count = 1;
J
Jeff King 已提交
3690
	spec->is_binary = -1;
J
Junio C Hamano 已提交
3691 3692 3693
	return spec;
}

3694 3695 3696 3697 3698 3699 3700 3701
void free_filespec(struct diff_filespec *spec)
{
	if (!--spec->count) {
		diff_free_filespec_data(spec);
		free(spec);
	}
}

3702 3703
void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,
		   int oid_valid, unsigned short mode)
J
Junio C Hamano 已提交
3704 3705 3706
{
	if (mode) {
		spec->mode = canon_mode(mode);
3707 3708
		oidcpy(&spec->oid, oid);
		spec->oid_valid = oid_valid;
J
Junio C Hamano 已提交
3709 3710 3711 3712
	}
}

/*
3713
 * Given a name and sha1 pair, if the index tells us the file in
J
Junio C Hamano 已提交
3714 3715 3716
 * the work tree has that object contents, return true, so that
 * prepare_temp_file() does not have to inflate and extract.
 */
3717
static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
J
Junio C Hamano 已提交
3718
{
3719
	const struct cache_entry *ce;
J
Junio C Hamano 已提交
3720 3721 3722
	struct stat st;
	int pos, len;

3723 3724
	/*
	 * We do not read the cache ourselves here, because the
J
Junio C Hamano 已提交
3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738
	 * benchmark with my previous version that always reads cache
	 * shows that it makes things worse for diff-tree comparing
	 * two linux-2.6 kernel trees in an already checked out work
	 * tree.  This is because most diff-tree comparisons deal with
	 * only a small number of files, while reading the cache is
	 * expensive for a large project, and its cost outweighs the
	 * savings we get by not inflating the object to a temporary
	 * file.  Practically, this code only helps when we are used
	 * by diff-cache --cached, which does read the cache before
	 * calling us.
	 */
	if (!active_cache)
		return 0;

3739 3740 3741 3742 3743 3744 3745 3746 3747
	/* We want to avoid the working directory if our caller
	 * doesn't need the data in a normal file, this system
	 * is rather slow with its stat/open/mmap/close syscalls,
	 * and the object is contained in a pack file.  The pack
	 * is probably already open and will be faster to obtain
	 * the data through than the working directory.  Loose
	 * objects however would tend to be slower as they need
	 * to be individually opened and inflated.
	 */
3748
	if (!FAST_WORKING_DIRECTORY && !want_file && has_object_pack(oid))
3749 3750
		return 0;

3751 3752 3753 3754
	/*
	 * Similarly, if we'd have to convert the file contents anyway, that
	 * makes the optimization not worthwhile.
	 */
3755
	if (!want_file && would_convert_to_git(&the_index, name))
3756 3757
		return 0;

J
Junio C Hamano 已提交
3758 3759 3760 3761 3762
	len = strlen(name);
	pos = cache_name_pos(name, len);
	if (pos < 0)
		return 0;
	ce = active_cache[pos];
3763 3764 3765 3766 3767

	/*
	 * This is not the sha1 we are looking for, or
	 * unreusable because it is not a regular file.
	 */
3768
	if (!oideq(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
J
Junio C Hamano 已提交
3769
		return 0;
3770

3771 3772 3773 3774
	/*
	 * If ce is marked as "assume unchanged", there is no
	 * guarantee that work tree matches what we are looking for.
	 */
3775
	if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
3776 3777
		return 0;

3778 3779
	/*
	 * If ce matches the file in the work tree, we can reuse it.
J
Junio C Hamano 已提交
3780
	 */
3781 3782 3783 3784 3785
	if (ce_uptodate(ce) ||
	    (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
		return 1;

	return 0;
J
Junio C Hamano 已提交
3786 3787
}

3788 3789
static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
{
J
Jeff King 已提交
3790 3791
	struct strbuf buf = STRBUF_INIT;
	char *dirty = "";
3792 3793

	/* Are we looking at the work tree? */
3794
	if (s->dirty_submodule)
3795 3796
		dirty = "-dirty";

3797 3798
	strbuf_addf(&buf, "Subproject commit %s%s\n",
		    oid_to_hex(&s->oid), dirty);
J
Jeff King 已提交
3799
	s->size = buf.len;
3800 3801
	if (size_only) {
		s->data = NULL;
J
Jeff King 已提交
3802 3803 3804 3805
		strbuf_release(&buf);
	} else {
		s->data = strbuf_detach(&buf, NULL);
		s->should_free = 1;
3806 3807 3808 3809
	}
	return 0;
}

J
Junio C Hamano 已提交
3810 3811 3812 3813 3814
/*
 * While doing rename detection and pickaxe operation, we may need to
 * grab the data for the blob (or file) for our own in-core comparison.
 * diff_filespec has data and size fields for this purpose.
 */
3815
int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
J
Junio C Hamano 已提交
3816
{
3817
	int size_only = flags & CHECK_SIZE_ONLY;
J
Junio C Hamano 已提交
3818
	int err = 0;
3819
	int conv_flags = global_conv_flags_eol;
3820 3821 3822 3823
	/*
	 * demote FAIL to WARN to allow inspecting the situation
	 * instead of refusing.
	 */
3824 3825
	if (conv_flags & CONV_EOL_RNDTRP_DIE)
		conv_flags = CONV_EOL_RNDTRP_WARN;
3826

J
Junio C Hamano 已提交
3827 3828 3829 3830 3831 3832
	if (!DIFF_FILE_VALID(s))
		die("internal error: asking to populate invalid file.");
	if (S_ISDIR(s->mode))
		return -1;

	if (s->data)
3833
		return 0;
3834

3835 3836 3837
	if (size_only && 0 < s->size)
		return 0;

M
Martin Waitz 已提交
3838
	if (S_ISGITLINK(s->mode))
3839 3840
		return diff_populate_gitlink(s, size_only);

3841
	if (!s->oid_valid ||
3842
	    reuse_worktree_file(s->path, &s->oid, 0)) {
3843
		struct strbuf buf = STRBUF_INIT;
J
Junio C Hamano 已提交
3844 3845
		struct stat st;
		int fd;
L
Linus Torvalds 已提交
3846

J
Junio C Hamano 已提交
3847
		if (lstat(s->path, &st) < 0) {
3848 3849 3850 3851 3852 3853
		err_empty:
			err = -1;
		empty:
			s->data = (char *)"";
			s->size = 0;
			return err;
J
Junio C Hamano 已提交
3854
		}
3855
		s->size = xsize_t(st.st_size);
J
Junio C Hamano 已提交
3856 3857 3858
		if (!s->size)
			goto empty;
		if (S_ISLNK(st.st_mode)) {
3859 3860 3861
			struct strbuf sb = STRBUF_INIT;

			if (strbuf_readlink(&sb, s->path, s->size))
J
Junio C Hamano 已提交
3862
				goto err_empty;
3863 3864
			s->size = sb.len;
			s->data = strbuf_detach(&sb, NULL);
3865
			s->should_free = 1;
J
Junio C Hamano 已提交
3866 3867
			return 0;
		}
3868 3869 3870 3871 3872 3873 3874

		/*
		 * Even if the caller would be happy with getting
		 * only the size, we cannot return early at this
		 * point if the path requires us to run the content
		 * conversion.
		 */
3875
		if (size_only && !would_convert_to_git(&the_index, s->path))
3876
			return 0;
3877 3878 3879 3880 3881 3882 3883 3884 3885 3886

		/*
		 * Note: this check uses xsize_t(st.st_size) that may
		 * not be the true size of the blob after it goes
		 * through convert_to_git().  This may not strictly be
		 * correct, but the whole point of big_file_threshold
		 * and is_binary check being that we want to avoid
		 * opening the file and inspecting the contents, this
		 * is probably fine.
		 */
3887 3888 3889 3890 3891
		if ((flags & CHECK_BINARY) &&
		    s->size > big_file_threshold && s->is_binary == -1) {
			s->is_binary = 1;
			return 0;
		}
J
Junio C Hamano 已提交
3892 3893 3894
		fd = open(s->path, O_RDONLY);
		if (fd < 0)
			goto err_empty;
3895
		s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
J
Junio C Hamano 已提交
3896 3897
		close(fd);
		s->should_munmap = 1;
L
Linus Torvalds 已提交
3898 3899 3900 3901

		/*
		 * Convert from working tree format to canonical git format
		 */
3902
		if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
3903
			size_t size = 0;
L
Linus Torvalds 已提交
3904 3905
			munmap(s->data, s->size);
			s->should_munmap = 0;
3906 3907
			s->data = strbuf_detach(&buf, &size);
			s->size = size;
L
Linus Torvalds 已提交
3908 3909
			s->should_free = 1;
		}
J
Junio C Hamano 已提交
3910 3911
	}
	else {
3912
		enum object_type type;
3913
		if (size_only || (flags & CHECK_BINARY)) {
3914 3915
			type = oid_object_info(the_repository, &s->oid,
					       &s->size);
3916
			if (type < 0)
3917 3918
				die("unable to read %s",
				    oid_to_hex(&s->oid));
3919 3920 3921 3922 3923 3924
			if (size_only)
				return 0;
			if (s->size > big_file_threshold && s->is_binary == -1) {
				s->is_binary = 1;
				return 0;
			}
J
Junio C Hamano 已提交
3925
		}
3926
		s->data = read_object_file(&s->oid, &type, &s->size);
3927
		if (!s->data)
3928
			die("unable to read %s", oid_to_hex(&s->oid));
3929
		s->should_free = 1;
J
Junio C Hamano 已提交
3930 3931 3932 3933
	}
	return 0;
}

3934
void diff_free_filespec_blob(struct diff_filespec *s)
J
Junio C Hamano 已提交
3935 3936 3937 3938 3939
{
	if (s->should_free)
		free(s->data);
	else if (s->should_munmap)
		munmap(s->data, s->size);
3940 3941 3942 3943 3944

	if (s->should_free || s->should_munmap) {
		s->should_free = s->should_munmap = 0;
		s->data = NULL;
	}
J
Jeff King 已提交
3945 3946 3947 3948
}

void diff_free_filespec_data(struct diff_filespec *s)
{
3949
	diff_free_filespec_blob(s);
3950
	FREE_AND_NULL(s->cnt_data);
J
Junio C Hamano 已提交
3951 3952
}

3953
static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
J
Junio C Hamano 已提交
3954 3955
			   void *blob,
			   unsigned long size,
3956
			   const struct object_id *oid,
J
Junio C Hamano 已提交
3957 3958
			   int mode)
{
3959
	struct strbuf buf = STRBUF_INIT;
3960
	struct strbuf tempfile = STRBUF_INIT;
3961 3962
	char *path_dup = xstrdup(path);
	const char *base = basename(path_dup);
J
Junio C Hamano 已提交
3963

3964
	/* Generate "XXXXXX_basename.ext" */
3965 3966
	strbuf_addstr(&tempfile, "XXXXXX_");
	strbuf_addstr(&tempfile, base);
3967

3968
	temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
3969
	if (!temp->tempfile)
3970
		die_errno("unable to create temp-file");
3971
	if (convert_to_working_tree(&the_index, path,
3972 3973 3974 3975
			(const char *)blob, (size_t)size, &buf)) {
		blob = buf.buf;
		size = buf.len;
	}
3976
	if (write_in_full(temp->tempfile->fd, blob, size) < 0 ||
3977
	    close_tempfile_gently(temp->tempfile))
3978
		die_errno("unable to write temp-file");
3979
	temp->name = get_tempfile_path(temp->tempfile);
3980
	oid_to_hex_r(temp->hex, oid);
3981
	xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
3982
	strbuf_release(&buf);
3983
	strbuf_release(&tempfile);
3984
	free(path_dup);
J
Junio C Hamano 已提交
3985 3986
}

3987 3988
static struct diff_tempfile *prepare_temp_file(const char *name,
		struct diff_filespec *one)
J
Junio C Hamano 已提交
3989
{
3990 3991
	struct diff_tempfile *temp = claim_diff_tempfile();

J
Junio C Hamano 已提交
3992 3993 3994 3995 3996 3997
	if (!DIFF_FILE_VALID(one)) {
	not_a_valid_file:
		/* A '-' entry produces this for file-2, and
		 * a '+' entry produces this for file-1.
		 */
		temp->name = "/dev/null";
3998 3999
		xsnprintf(temp->hex, sizeof(temp->hex), ".");
		xsnprintf(temp->mode, sizeof(temp->mode), ".");
4000 4001 4002
		return temp;
	}

4003
	if (!S_ISGITLINK(one->mode) &&
4004
	    (!one->oid_valid ||
4005
	     reuse_worktree_file(name, &one->oid, 1))) {
J
Junio C Hamano 已提交
4006 4007 4008 4009
		struct stat st;
		if (lstat(name, &st) < 0) {
			if (errno == ENOENT)
				goto not_a_valid_file;
4010
			die_errno("stat(%s)", name);
J
Junio C Hamano 已提交
4011 4012
		}
		if (S_ISLNK(st.st_mode)) {
4013 4014
			struct strbuf sb = STRBUF_INIT;
			if (strbuf_readlink(&sb, name, st.st_size) < 0)
4015
				die_errno("readlink(%s)", name);
4016
			prep_temp_blob(name, temp, sb.buf, sb.len,
4017
				       (one->oid_valid ?
4018
					&one->oid : &null_oid),
4019
				       (one->oid_valid ?
J
Junio C Hamano 已提交
4020
					one->mode : S_IFLNK));
4021
			strbuf_release(&sb);
J
Junio C Hamano 已提交
4022 4023 4024 4025
		}
		else {
			/* we can borrow from the file in the work tree */
			temp->name = name;
4026
			if (!one->oid_valid)
4027
				oid_to_hex_r(temp->hex, &null_oid);
J
Junio C Hamano 已提交
4028
			else
4029
				oid_to_hex_r(temp->hex, &one->oid);
J
Junio C Hamano 已提交
4030 4031 4032
			/* Even though we may sometimes borrow the
			 * contents from the work tree, we always want
			 * one->mode.  mode is trustworthy even when
4033
			 * !(one->oid_valid), as long as
J
Junio C Hamano 已提交
4034 4035
			 * DIFF_FILE_VALID(one).
			 */
4036
			xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode);
J
Junio C Hamano 已提交
4037
		}
4038
		return temp;
J
Junio C Hamano 已提交
4039 4040 4041 4042
	}
	else {
		if (diff_populate_filespec(one, 0))
			die("cannot read data blob for %s", one->path);
4043
		prep_temp_blob(name, temp, one->data, one->size,
4044
			       &one->oid, one->mode);
J
Junio C Hamano 已提交
4045
	}
4046
	return temp;
J
Junio C Hamano 已提交
4047 4048
}

4049 4050 4051 4052 4053 4054 4055 4056 4057 4058
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);
}

J
Junio C Hamano 已提交
4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070
/* An external diff command takes:
 *
 * diff-cmd name infile1 infile1-sha1 infile1-mode \
 *               infile2 infile2-sha1 infile2-mode [ rename-to ]
 *
 */
static void run_external_diff(const char *pgm,
			      const char *name,
			      const char *other,
			      struct diff_filespec *one,
			      struct diff_filespec *two,
			      const char *xfrm_msg,
4071 4072
			      int complete_rewrite,
			      struct diff_options *o)
J
Junio C Hamano 已提交
4073
{
4074
	struct argv_array argv = ARGV_ARRAY_INIT;
4075
	struct argv_array env = ARGV_ARRAY_INIT;
4076
	struct diff_queue_struct *q = &diff_queued_diff;
J
Junio C Hamano 已提交
4077

4078 4079
	argv_array_push(&argv, pgm);
	argv_array_push(&argv, name);
J
Junio C Hamano 已提交
4080 4081

	if (one && two) {
4082 4083 4084 4085 4086
		add_external_diff_name(&argv, name, one);
		if (!other)
			add_external_diff_name(&argv, name, two);
		else {
			add_external_diff_name(&argv, other, two);
4087 4088
			argv_array_push(&argv, other);
			argv_array_push(&argv, xfrm_msg);
J
Junio C Hamano 已提交
4089 4090
		}
	}
4091

4092 4093
	argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
	argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
4094

4095 4096
	if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
		die(_("external diff died, stopping at %s"), name);
4097

J
Junio C Hamano 已提交
4098
	remove_tempfile();
4099
	argv_array_clear(&argv);
4100
	argv_array_clear(&env);
J
Junio C Hamano 已提交
4101 4102
}

4103 4104 4105 4106 4107
static int similarity_index(struct diff_filepair *p)
{
	return p->score * 100 / MAX_SCORE;
}

4108 4109 4110
static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
{
	if (startup_info->have_repository)
4111
		return find_unique_abbrev(oid, abbrev);
4112 4113
	else {
		char *hex = oid_to_hex(oid);
4114 4115
		if (abbrev < 0)
			abbrev = FALLBACK_DEFAULT_ABBREV;
4116
		if (abbrev > the_hash_algo->hexsz)
4117
			BUG("oid abbreviation out of range: %d", abbrev);
4118 4119
		if (abbrev)
			hex[abbrev] = '\0';
4120 4121 4122 4123
		return hex;
	}
}

4124 4125 4126 4127 4128 4129
static void fill_metainfo(struct strbuf *msg,
			  const char *name,
			  const char *other,
			  struct diff_filespec *one,
			  struct diff_filespec *two,
			  struct diff_options *o,
4130
			  struct diff_filepair *p,
4131
			  int *must_show_header,
4132
			  int use_color)
4133
{
4134 4135
	const char *set = diff_get_color(use_color, DIFF_METAINFO);
	const char *reset = diff_get_color(use_color, DIFF_RESET);
4136
	const char *line_prefix = diff_line_prefix(o);
4137

4138
	*must_show_header = 1;
4139 4140 4141
	strbuf_init(msg, PATH_MAX * 2 + 300);
	switch (p->status) {
	case DIFF_STATUS_COPIED:
J
Junio C Hamano 已提交
4142 4143 4144 4145
		strbuf_addf(msg, "%s%ssimilarity index %d%%",
			    line_prefix, set, similarity_index(p));
		strbuf_addf(msg, "%s\n%s%scopy from ",
			    reset,  line_prefix, set);
4146
		quote_c_style(name, msg, NULL, 0);
J
Junio C Hamano 已提交
4147
		strbuf_addf(msg, "%s\n%s%scopy to ", reset, line_prefix, set);
4148
		quote_c_style(other, msg, NULL, 0);
4149
		strbuf_addf(msg, "%s\n", reset);
4150 4151
		break;
	case DIFF_STATUS_RENAMED:
J
Junio C Hamano 已提交
4152 4153 4154 4155
		strbuf_addf(msg, "%s%ssimilarity index %d%%",
			    line_prefix, set, similarity_index(p));
		strbuf_addf(msg, "%s\n%s%srename from ",
			    reset, line_prefix, set);
4156
		quote_c_style(name, msg, NULL, 0);
J
Junio C Hamano 已提交
4157 4158
		strbuf_addf(msg, "%s\n%s%srename to ",
			    reset, line_prefix, set);
4159
		quote_c_style(other, msg, NULL, 0);
4160
		strbuf_addf(msg, "%s\n", reset);
4161 4162 4163
		break;
	case DIFF_STATUS_MODIFIED:
		if (p->score) {
J
Junio C Hamano 已提交
4164 4165
			strbuf_addf(msg, "%s%sdissimilarity index %d%%%s\n",
				    line_prefix,
4166
				    set, similarity_index(p), reset);
4167 4168 4169 4170
			break;
		}
		/* fallthru */
	default:
4171
		*must_show_header = 0;
4172
	}
4173
	if (one && two && !oideq(&one->oid, &two->oid)) {
4174 4175
		const unsigned hexsz = the_hash_algo->hexsz;
		int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV;
4176

4177
		if (o->flags.binary) {
4178 4179 4180
			mmfile_t mf;
			if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
			    (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
4181
				abbrev = hexsz;
4182
		}
4183 4184 4185
		strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
			    diff_abbrev_oid(&one->oid, abbrev),
			    diff_abbrev_oid(&two->oid, abbrev));
4186 4187
		if (one->mode == two->mode)
			strbuf_addf(msg, " %06o", one->mode);
4188
		strbuf_addf(msg, "%s\n", reset);
4189 4190 4191
	}
}

J
Junio C Hamano 已提交
4192 4193 4194
static void run_diff_cmd(const char *pgm,
			 const char *name,
			 const char *other,
4195
			 const char *attr_path,
J
Junio C Hamano 已提交
4196 4197
			 struct diff_filespec *one,
			 struct diff_filespec *two,
4198
			 struct strbuf *msg,
J
Junio C Hamano 已提交
4199
			 struct diff_options *o,
4200
			 struct diff_filepair *p)
J
Junio C Hamano 已提交
4201
{
4202 4203
	const char *xfrm_msg = NULL;
	int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
4204
	int must_show_header = 0;
4205

4206

4207
	if (o->flags.allow_external) {
4208 4209 4210
		struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
		if (drv && drv->external)
			pgm = drv->external;
J
Junio C Hamano 已提交
4211 4212
	}

4213 4214 4215 4216 4217 4218
	if (msg) {
		/*
		 * don't use colors when the header is intended for an
		 * external diff driver
		 */
		fill_metainfo(msg, name, other, one, two, o, p,
4219
			      &must_show_header,
4220
			      want_color(o->use_color) && !pgm);
4221 4222 4223
		xfrm_msg = msg->len ? msg->buf : NULL;
	}

J
Junio C Hamano 已提交
4224 4225
	if (pgm) {
		run_external_diff(pgm, name, other, one, two, xfrm_msg,
4226
				  complete_rewrite, o);
J
Junio C Hamano 已提交
4227 4228 4229 4230
		return;
	}
	if (one && two)
		builtin_diff(name, other ? other : name,
4231 4232
			     one, two, xfrm_msg, must_show_header,
			     o, complete_rewrite);
J
Junio C Hamano 已提交
4233
	else
4234
		fprintf(o->file, "* Unmerged path %s\n", name);
J
Junio C Hamano 已提交
4235 4236
}

4237
static void diff_fill_oid_info(struct diff_filespec *one)
J
Junio C Hamano 已提交
4238 4239
{
	if (DIFF_FILE_VALID(one)) {
4240
		if (!one->oid_valid) {
J
Junio C Hamano 已提交
4241
			struct stat st;
4242
			if (one->is_stdin) {
4243
				oidclr(&one->oid);
4244 4245
				return;
			}
J
Junio C Hamano 已提交
4246
			if (lstat(one->path, &st) < 0)
4247
				die_errno("stat '%s'", one->path);
4248
			if (index_path(&one->oid, one->path, &st, 0))
4249
				die("cannot hash %s", one->path);
J
Junio C Hamano 已提交
4250 4251 4252
		}
	}
	else
4253
		oidclr(&one->oid);
J
Junio C Hamano 已提交
4254 4255
}

4256 4257 4258
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
{
	/* Strip the prefix but do not molest /dev/null and absolute paths */
4259
	if (*namep && **namep != '/') {
4260
		*namep += prefix_length;
4261 4262 4263 4264
		if (**namep == '/')
			++*namep;
	}
	if (*otherp && **otherp != '/') {
4265
		*otherp += prefix_length;
4266 4267 4268
		if (**otherp == '/')
			++*otherp;
	}
4269 4270
}

J
Junio C Hamano 已提交
4271 4272 4273
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
	const char *pgm = external_diff();
4274 4275 4276
	struct strbuf msg;
	struct diff_filespec *one = p->one;
	struct diff_filespec *two = p->two;
J
Junio C Hamano 已提交
4277 4278
	const char *name;
	const char *other;
4279
	const char *attr_path;
4280

S
Stefan Beller 已提交
4281 4282
	name  = one->path;
	other = (strcmp(name, two->path) ? two->path : NULL);
4283 4284 4285
	attr_path = name;
	if (o->prefix_length)
		strip_prefix(o->prefix_length, &name, &other);
J
Junio C Hamano 已提交
4286

4287
	if (!o->flags.allow_external)
4288 4289
		pgm = NULL;

J
Junio C Hamano 已提交
4290
	if (DIFF_PAIR_UNMERGED(p)) {
4291
		run_diff_cmd(pgm, name, NULL, attr_path,
4292
			     NULL, NULL, NULL, o, p);
J
Junio C Hamano 已提交
4293 4294 4295
		return;
	}

4296 4297
	diff_fill_oid_info(one);
	diff_fill_oid_info(two);
J
Junio C Hamano 已提交
4298 4299 4300 4301

	if (!pgm &&
	    DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
	    (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
4302 4303
		/*
		 * a filepair that changes between file and symlink
J
Junio C Hamano 已提交
4304 4305 4306
		 * needs to be split into deletion and creation.
		 */
		struct diff_filespec *null = alloc_filespec(two->path);
4307
		run_diff_cmd(NULL, name, other, attr_path,
4308
			     one, null, &msg, o, p);
J
Junio C Hamano 已提交
4309
		free(null);
4310 4311
		strbuf_release(&msg);

J
Junio C Hamano 已提交
4312
		null = alloc_filespec(one->path);
4313
		run_diff_cmd(NULL, name, other, attr_path,
4314
			     null, two, &msg, o, p);
J
Junio C Hamano 已提交
4315 4316 4317
		free(null);
	}
	else
4318
		run_diff_cmd(pgm, name, other, attr_path,
4319
			     one, two, &msg, o, p);
J
Junio C Hamano 已提交
4320

4321
	strbuf_release(&msg);
J
Junio C Hamano 已提交
4322 4323 4324 4325 4326 4327 4328 4329 4330 4331
}

static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
			 struct diffstat_t *diffstat)
{
	const char *name;
	const char *other;

	if (DIFF_PAIR_UNMERGED(p)) {
		/* unmerged */
4332
		builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p);
J
Junio C Hamano 已提交
4333 4334 4335 4336 4337 4338
		return;
	}

	name = p->one->path;
	other = (strcmp(name, p->two->path) ? p->two->path : NULL);

4339 4340 4341
	if (o->prefix_length)
		strip_prefix(o->prefix_length, &name, &other);

4342 4343
	diff_fill_oid_info(p->one);
	diff_fill_oid_info(p->two);
J
Junio C Hamano 已提交
4344

4345
	builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
J
Junio C Hamano 已提交
4346 4347
}

4348 4349 4350 4351
static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
{
	const char *name;
	const char *other;
4352
	const char *attr_path;
4353 4354 4355 4356 4357 4358 4359 4360

	if (DIFF_PAIR_UNMERGED(p)) {
		/* unmerged */
		return;
	}

	name = p->one->path;
	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
4361 4362 4363 4364
	attr_path = other ? other : name;

	if (o->prefix_length)
		strip_prefix(o->prefix_length, &name, &other);
4365

4366 4367
	diff_fill_oid_info(p->one);
	diff_fill_oid_info(p->two);
4368

4369
	builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
4370 4371
}

J
Junio C Hamano 已提交
4372 4373
void diff_setup(struct diff_options *options)
{
4374
	memcpy(options, &default_diff_options, sizeof(*options));
4375 4376 4377

	options->file = stdout;

4378
	options->abbrev = DEFAULT_ABBREV;
J
Junio C Hamano 已提交
4379 4380 4381
	options->line_termination = '\n';
	options->break_opt = -1;
	options->rename_limit = -1;
4382
	options->dirstat_permille = diff_dirstat_permille_default;
4383
	options->context = diff_context_default;
4384
	options->interhunkcontext = diff_interhunk_context_default;
4385
	options->ws_error_highlight = ws_error_highlight_default;
4386
	options->flags.rename_empty = 1;
4387
	options->objfind = NULL;
J
Junio C Hamano 已提交
4388

4389
	/* pathchange left =NULL by default */
J
Junio C Hamano 已提交
4390 4391
	options->change = diff_change;
	options->add_remove = diff_addremove;
4392
	options->use_color = diff_use_color_default;
4393
	options->detect_rename = diff_detect_rename_default;
4394
	options->xdl_opts |= diff_algorithm;
4395 4396
	if (diff_indent_heuristic)
		DIFF_XDL_SET(options, INDENT_HEURISTIC);
4397

4398 4399
	options->orderfile = diff_order_file_cfg;

4400 4401 4402
	if (diff_no_prefix) {
		options->a_prefix = options->b_prefix = "";
	} else if (!diff_mnemonic_prefix) {
4403 4404 4405
		options->a_prefix = "a/";
		options->b_prefix = "b/";
	}
4406 4407

	options->color_moved = diff_color_moved_default;
4408
	options->color_moved_ws_handling = diff_color_moved_ws_default;
J
Junio C Hamano 已提交
4409 4410
}

T
Thomas Rast 已提交
4411
void diff_setup_done(struct diff_options *options)
J
Junio C Hamano 已提交
4412
{
4413 4414 4415 4416
	unsigned check_mask = DIFF_FORMAT_NAME |
			      DIFF_FORMAT_NAME_STATUS |
			      DIFF_FORMAT_CHECKDIFF |
			      DIFF_FORMAT_NO_OUTPUT;
4417 4418 4419 4420 4421
	/*
	 * This must be signed because we're comparing against a potentially
	 * negative value.
	 */
	const int hexsz = the_hash_algo->hexsz;
4422

4423 4424 4425
	if (options->set_default)
		options->set_default(options);

4426
	if (HAS_MULTI_BITS(options->output_format & check_mask))
4427
		die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
4428

4429 4430 4431
	if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
		die(_("-G, -S and --find-object are mutually exclusive"));

4432 4433 4434 4435
	/*
	 * Most of the time we can say "there are changes"
	 * only by checking if there are changed paths, but
	 * --ignore-whitespace* options force us to look
J
Jim Meyering 已提交
4436
	 * inside contents.
4437 4438
	 */

4439
	if ((options->xdl_opts & XDF_WHITESPACE_FLAGS))
4440
		options->flags.diff_from_contents = 1;
4441
	else
4442
		options->flags.diff_from_contents = 0;
4443

4444
	if (options->flags.find_copies_harder)
4445 4446
		options->detect_rename = DIFF_DETECT_COPY;

4447
	if (!options->flags.relative_name)
4448 4449 4450 4451 4452 4453
		options->prefix = NULL;
	if (options->prefix)
		options->prefix_length = strlen(options->prefix);
	else
		options->prefix_length = 0;

4454 4455 4456 4457 4458
	if (options->output_format & (DIFF_FORMAT_NAME |
				      DIFF_FORMAT_NAME_STATUS |
				      DIFF_FORMAT_CHECKDIFF |
				      DIFF_FORMAT_NO_OUTPUT))
		options->output_format &= ~(DIFF_FORMAT_RAW |
J
Junio C Hamano 已提交
4459
					    DIFF_FORMAT_NUMSTAT |
4460
					    DIFF_FORMAT_DIFFSTAT |
4461
					    DIFF_FORMAT_SHORTSTAT |
4462
					    DIFF_FORMAT_DIRSTAT |
4463 4464 4465
					    DIFF_FORMAT_SUMMARY |
					    DIFF_FORMAT_PATCH);

J
Junio C Hamano 已提交
4466 4467 4468 4469
	/*
	 * These cases always need recursive; we do not drop caller-supplied
	 * recursive bits for other formats here.
	 */
4470
	if (options->output_format & (DIFF_FORMAT_PATCH |
J
Junio C Hamano 已提交
4471
				      DIFF_FORMAT_NUMSTAT |
4472
				      DIFF_FORMAT_DIFFSTAT |
4473
				      DIFF_FORMAT_SHORTSTAT |
4474
				      DIFF_FORMAT_DIRSTAT |
4475
				      DIFF_FORMAT_SUMMARY |
4476
				      DIFF_FORMAT_CHECKDIFF))
4477
		options->flags.recursive = 1;
4478
	/*
4479
	 * Also pickaxe would not work very well if you do not say recursive
4480
	 */
4481
	if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)
4482
		options->flags.recursive = 1;
4483 4484 4485 4486 4487
	/*
	 * When patches are generated, submodules diffed against the work tree
	 * must be checked for dirtiness too so it can be shown in the output
	 */
	if (options->output_format & DIFF_FORMAT_PATCH)
4488
		options->flags.dirty_submodules = 1;
4489

J
Junio C Hamano 已提交
4490 4491
	if (options->detect_rename && options->rename_limit < 0)
		options->rename_limit = diff_rename_limit_default;
4492 4493
	if (hexsz < options->abbrev)
		options->abbrev = hexsz; /* full */
J
Junio C Hamano 已提交
4494

J
Junio C Hamano 已提交
4495 4496 4497 4498 4499
	/*
	 * It does not make sense to show the first hit we happened
	 * to have found.  It does not make sense not to return with
	 * exit code in such a case either.
	 */
4500
	if (options->flags.quick) {
J
Junio C Hamano 已提交
4501
		options->output_format = DIFF_FORMAT_NO_OUTPUT;
4502
		options->flags.exit_with_status = 1;
J
Junio C Hamano 已提交
4503
	}
4504

4505
	options->diff_path_counter = 0;
4506

4507
	if (options->flags.follow_renames && options->pathspec.nr != 1)
4508
		die(_("--follow requires exactly one pathspec"));
4509 4510 4511

	if (!options->use_color || external_diff())
		options->color_moved = 0;
J
Junio C Hamano 已提交
4512 4513
}

T
Timo Hirvonen 已提交
4514
static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540
{
	char c, *eq;
	int len;

	if (*arg != '-')
		return 0;
	c = *++arg;
	if (!c)
		return 0;
	if (c == arg_short) {
		c = *++arg;
		if (!c)
			return 1;
		if (val && isdigit(c)) {
			char *end;
			int n = strtoul(arg, &end, 10);
			if (*end)
				return 0;
			*val = n;
			return 1;
		}
		return 0;
	}
	if (c != '-')
		return 0;
	arg++;
4541 4542
	eq = strchrnul(arg, '=');
	len = eq - arg;
4543 4544
	if (!len || strncmp(arg, arg_long, len))
		return 0;
4545
	if (*eq) {
4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557
		int n;
		char *end;
		if (!isdigit(*++eq))
			return 0;
		n = strtoul(eq, &end, 10);
		if (*end)
			return 0;
		*val = n;
	}
	return 1;
}

J
Junio C Hamano 已提交
4558 4559
static int diff_scoreopt_parse(const char *opt);

4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579
static inline int short_opt(char opt, const char **argv,
			    const char **optarg)
{
	const char *arg = argv[0];
	if (arg[0] != '-' || arg[1] != opt)
		return 0;
	if (arg[2] != '\0') {
		*optarg = arg + 2;
		return 1;
	}
	if (!argv[1])
		die("Option '%c' requires a value", opt);
	*optarg = argv[1];
	return 2;
}

int parse_long_opt(const char *opt, const char **argv,
		   const char **optarg)
{
	const char *arg = argv[0];
4580
	if (!skip_prefix(arg, "--", &arg))
4581
		return 0;
4582
	if (!skip_prefix(arg, opt, &arg))
4583
		return 0;
4584
	if (*arg == '=') { /* stuck form: --option=value */
4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596
		*optarg = arg + 1;
		return 1;
	}
	if (*arg != '\0')
		return 0;
	/* separate form: --option value */
	if (!argv[1])
		die("Option '--%s' requires a value", opt);
	*optarg = argv[1];
	return 2;
}

4597 4598 4599 4600 4601 4602
static int stat_opt(struct diff_options *options, const char **av)
{
	const char *arg = av[0];
	char *end;
	int width = options->stat_width;
	int name_width = options->stat_name_width;
4603
	int graph_width = options->stat_graph_width;
4604
	int count = options->stat_count;
4605
	int argcount = 1;
4606

J
Jeff King 已提交
4607
	if (!skip_prefix(arg, "--stat", &arg))
4608
		BUG("stat option does not begin with --stat: %s", arg);
4609 4610 4611 4612
	end = (char *)arg;

	switch (*arg) {
	case '-':
4613
		if (skip_prefix(arg, "-width", &arg)) {
4614 4615 4616
			if (*arg == '=')
				width = strtoul(arg + 1, &end, 10);
			else if (!*arg && !av[1])
4617
				die_want_option("--stat-width");
4618 4619 4620 4621
			else if (!*arg) {
				width = strtoul(av[1], &end, 10);
				argcount = 2;
			}
4622
		} else if (skip_prefix(arg, "-name-width", &arg)) {
4623 4624 4625
			if (*arg == '=')
				name_width = strtoul(arg + 1, &end, 10);
			else if (!*arg && !av[1])
4626
				die_want_option("--stat-name-width");
4627 4628 4629 4630
			else if (!*arg) {
				name_width = strtoul(av[1], &end, 10);
				argcount = 2;
			}
4631
		} else if (skip_prefix(arg, "-graph-width", &arg)) {
4632 4633 4634
			if (*arg == '=')
				graph_width = strtoul(arg + 1, &end, 10);
			else if (!*arg && !av[1])
4635
				die_want_option("--stat-graph-width");
4636 4637 4638 4639
			else if (!*arg) {
				graph_width = strtoul(av[1], &end, 10);
				argcount = 2;
			}
4640
		} else if (skip_prefix(arg, "-count", &arg)) {
4641 4642 4643
			if (*arg == '=')
				count = strtoul(arg + 1, &end, 10);
			else if (!*arg && !av[1])
4644
				die_want_option("--stat-count");
4645 4646 4647 4648
			else if (!*arg) {
				count = strtoul(av[1], &end, 10);
				argcount = 2;
			}
4649
		}
4650 4651 4652 4653 4654
		break;
	case '=':
		width = strtoul(arg+1, &end, 10);
		if (*end == ',')
			name_width = strtoul(end+1, &end, 10);
4655 4656
		if (*end == ',')
			count = strtoul(end+1, &end, 10);
4657 4658 4659 4660 4661 4662 4663
	}

	/* Important! This checks all the error cases! */
	if (*end)
		return 0;
	options->output_format |= DIFF_FORMAT_DIFFSTAT;
	options->stat_name_width = name_width;
4664
	options->stat_graph_width = graph_width;
4665
	options->stat_width = width;
4666
	options->stat_count = count;
4667
	return argcount;
4668 4669
}

4670 4671
static int parse_dirstat_opt(struct diff_options *options, const char *params)
{
4672 4673
	struct strbuf errmsg = STRBUF_INIT;
	if (parse_dirstat_params(options, params, &errmsg))
4674
		die(_("Failed to parse --dirstat/-X option parameter:\n%s"),
4675 4676
		    errmsg.buf);
	strbuf_release(&errmsg);
4677 4678 4679 4680 4681 4682 4683 4684
	/*
	 * 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;
}

4685 4686 4687 4688 4689 4690 4691 4692
static int parse_submodule_opt(struct diff_options *options, const char *value)
{
	if (parse_submodule_params(options, value))
		die(_("Failed to parse --submodule option parameter: '%s'"),
			value);
	return 1;
}

4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728
static const char diff_status_letters[] = {
	DIFF_STATUS_ADDED,
	DIFF_STATUS_COPIED,
	DIFF_STATUS_DELETED,
	DIFF_STATUS_MODIFIED,
	DIFF_STATUS_RENAMED,
	DIFF_STATUS_TYPE_CHANGED,
	DIFF_STATUS_UNKNOWN,
	DIFF_STATUS_UNMERGED,
	DIFF_STATUS_FILTER_AON,
	DIFF_STATUS_FILTER_BROKEN,
	'\0',
};

static unsigned int filter_bit['Z' + 1];

static void prepare_filter_bits(void)
{
	int i;

	if (!filter_bit[DIFF_STATUS_ADDED]) {
		for (i = 0; diff_status_letters[i]; i++)
			filter_bit[(int) diff_status_letters[i]] = (1 << i);
	}
}

static unsigned filter_bit_tst(char status, const struct diff_options *opt)
{
	return opt->filter & filter_bit[(int) status];
}

static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
{
	int i, optch;

	prepare_filter_bits();
4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744

	/*
	 * If there is a negation e.g. 'd' in the input, and we haven't
	 * initialized the filter field with another --diff-filter, start
	 * from full set of bits, except for AON.
	 */
	if (!opt->filter) {
		for (i = 0; (optch = optarg[i]) != '\0'; i++) {
			if (optch < 'a' || 'z' < optch)
				continue;
			opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1;
			opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON];
			break;
		}
	}

4745 4746
	for (i = 0; (optch = optarg[i]) != '\0'; i++) {
		unsigned int bit;
4747 4748 4749 4750 4751 4752 4753 4754
		int negate;

		if ('a' <= optch && optch <= 'z') {
			negate = 1;
			optch = toupper(optch);
		} else {
			negate = 0;
		}
4755 4756 4757

		bit = (0 <= optch && optch <= 'Z') ? filter_bit[optch] : 0;
		if (!bit)
4758
			return optarg[i];
4759 4760 4761 4762
		if (negate)
			opt->filter &= ~bit;
		else
			opt->filter |= bit;
4763 4764 4765 4766
	}
	return 0;
}

4767 4768 4769 4770 4771
static void enable_patch_output(int *fmt) {
	*fmt &= ~DIFF_FORMAT_NO_OUTPUT;
	*fmt |= DIFF_FORMAT_PATCH;
}

4772
static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *arg)
4773
{
4774
	int val = parse_ws_error_highlight(arg);
4775

4776 4777 4778 4779
	if (val < 0) {
		error("unknown value after ws-error-highlight=%.*s",
		      -1 - val, arg);
		return 0;
4780 4781 4782 4783 4784
	}
	opt->ws_error_highlight = val;
	return 1;
}

4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801
static int parse_objfind_opt(struct diff_options *opt, const char *arg)
{
	struct object_id oid;

	if (get_oid(arg, &oid))
		return error("unable to resolve '%s'", arg);

	if (!opt->objfind)
		opt->objfind = xcalloc(1, sizeof(*opt->objfind));

	opt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND;
	opt->flags.recursive = 1;
	opt->flags.tree_in_recursive = 1;
	oidset_insert(opt->objfind, &oid);
	return 1;
}

4802 4803
int diff_opt_parse(struct diff_options *options,
		   const char **av, int ac, const char *prefix)
J
Junio C Hamano 已提交
4804 4805
{
	const char *arg = av[0];
4806 4807
	const char *optarg;
	int argcount;
4808

4809 4810 4811
	if (!prefix)
		prefix = "";

4812
	/* Output format options */
4813 4814 4815
	if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
	    || opt_arg(arg, 'U', "unified", &options->context))
		enable_patch_output(&options->output_format);
4816 4817
	else if (!strcmp(arg, "--raw"))
		options->output_format |= DIFF_FORMAT_RAW;
4818 4819 4820 4821
	else if (!strcmp(arg, "--patch-with-raw")) {
		enable_patch_output(&options->output_format);
		options->output_format |= DIFF_FORMAT_RAW;
	} else if (!strcmp(arg, "--numstat"))
J
Junio C Hamano 已提交
4822
		options->output_format |= DIFF_FORMAT_NUMSTAT;
4823
	else if (!strcmp(arg, "--shortstat"))
4824
		options->output_format |= DIFF_FORMAT_SHORTSTAT;
4825 4826
	else if (skip_prefix(arg, "-X", &arg) ||
		 skip_to_optional_arg(arg, "--dirstat", &arg))
4827
		return parse_dirstat_opt(options, arg);
4828 4829
	else if (!strcmp(arg, "--cumulative"))
		return parse_dirstat_opt(options, "cumulative");
4830
	else if (skip_to_optional_arg(arg, "--dirstat-by-file", &arg)) {
4831
		parse_dirstat_opt(options, "files");
4832
		return parse_dirstat_opt(options, arg);
4833
	}
4834 4835 4836 4837
	else if (!strcmp(arg, "--check"))
		options->output_format |= DIFF_FORMAT_CHECKDIFF;
	else if (!strcmp(arg, "--summary"))
		options->output_format |= DIFF_FORMAT_SUMMARY;
4838 4839 4840 4841
	else if (!strcmp(arg, "--patch-with-stat")) {
		enable_patch_output(&options->output_format);
		options->output_format |= DIFF_FORMAT_DIFFSTAT;
	} else if (!strcmp(arg, "--name-only"))
4842 4843 4844
		options->output_format |= DIFF_FORMAT_NAME;
	else if (!strcmp(arg, "--name-status"))
		options->output_format |= DIFF_FORMAT_NAME_STATUS;
4845
	else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
4846
		options->output_format |= DIFF_FORMAT_NO_OUTPUT;
4847
	else if (starts_with(arg, "--stat"))
4848
		/* --stat, --stat-width, --stat-name-width, or --stat-count */
4849
		return stat_opt(options, av);
4850 4851 4852 4853 4854
	else if (!strcmp(arg, "--compact-summary")) {
		 options->flags.stat_with_summary = 1;
		 options->output_format |= DIFF_FORMAT_DIFFSTAT;
	} else if (!strcmp(arg, "--no-compact-summary"))
		 options->flags.stat_with_summary = 0;
4855 4856

	/* renames options */
4857 4858
	else if (starts_with(arg, "-B") ||
		 skip_to_optional_arg(arg, "--break-rewrites", NULL)) {
4859
		if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
J
Jeff King 已提交
4860
			return error("invalid argument to -B: %s", arg+2);
J
Junio C Hamano 已提交
4861
	}
4862 4863
	else if (starts_with(arg, "-M") ||
		 skip_to_optional_arg(arg, "--find-renames", NULL)) {
4864
		if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
J
Jeff King 已提交
4865
			return error("invalid argument to -M: %s", arg+2);
J
Junio C Hamano 已提交
4866 4867
		options->detect_rename = DIFF_DETECT_RENAME;
	}
4868 4869 4870
	else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
		options->irreversible_delete = 1;
	}
4871 4872
	else if (starts_with(arg, "-C") ||
		 skip_to_optional_arg(arg, "--find-copies", NULL)) {
4873
		if (options->detect_rename == DIFF_DETECT_COPY)
4874
			options->flags.find_copies_harder = 1;
4875
		if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
J
Jeff King 已提交
4876
			return error("invalid argument to -C: %s", arg+2);
J
Junio C Hamano 已提交
4877 4878
		options->detect_rename = DIFF_DETECT_COPY;
	}
4879 4880
	else if (!strcmp(arg, "--no-renames"))
		options->detect_rename = 0;
4881
	else if (!strcmp(arg, "--rename-empty"))
4882
		options->flags.rename_empty = 1;
4883
	else if (!strcmp(arg, "--no-rename-empty"))
4884
		options->flags.rename_empty = 0;
4885
	else if (skip_to_optional_arg_default(arg, "--relative", &arg, NULL)) {
4886
		options->flags.relative_name = 1;
4887 4888
		if (arg)
			options->prefix = arg;
4889
	}
4890 4891

	/* xdiff options */
4892 4893 4894 4895
	else if (!strcmp(arg, "--minimal"))
		DIFF_XDL_SET(options, NEED_MINIMAL);
	else if (!strcmp(arg, "--no-minimal"))
		DIFF_XDL_CLR(options, NEED_MINIMAL);
4896
	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
4897
		DIFF_XDL_SET(options, IGNORE_WHITESPACE);
4898
	else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
4899
		DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
4900
	else if (!strcmp(arg, "--ignore-space-at-eol"))
4901
		DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
J
Junio C Hamano 已提交
4902 4903
	else if (!strcmp(arg, "--ignore-cr-at-eol"))
		DIFF_XDL_SET(options, IGNORE_CR_AT_EOL);
4904 4905
	else if (!strcmp(arg, "--ignore-blank-lines"))
		DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
4906
	else if (!strcmp(arg, "--indent-heuristic"))
4907
		DIFF_XDL_SET(options, INDENT_HEURISTIC);
4908
	else if (!strcmp(arg, "--no-indent-heuristic"))
4909
		DIFF_XDL_CLR(options, INDENT_HEURISTIC);
J
Jonathan Tan 已提交
4910 4911
	else if (!strcmp(arg, "--patience")) {
		int i;
4912
		options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
J
Jonathan Tan 已提交
4913 4914 4915 4916 4917 4918 4919 4920 4921
		/*
		 * Both --patience and --anchored use PATIENCE_DIFF
		 * internally, so remove any anchors previously
		 * specified.
		 */
		for (i = 0; i < options->anchors_nr; i++)
			free(options->anchors[i]);
		options->anchors_nr = 0;
	} else if (!strcmp(arg, "--histogram"))
4922
		options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
4923 4924
	else if ((argcount = parse_long_opt("diff-algorithm", av, &optarg))) {
		long value = parse_algorithm_value(optarg);
4925 4926 4927 4928 4929 4930 4931
		if (value < 0)
			return error("option diff-algorithm accepts \"myers\", "
				     "\"minimal\", \"patience\" and \"histogram\"");
		/* clear out previous settings */
		DIFF_XDL_CLR(options, NEED_MINIMAL);
		options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
		options->xdl_opts |= value;
4932
		return argcount;
J
Jonathan Tan 已提交
4933 4934 4935 4936 4937
	} else if (skip_prefix(arg, "--anchored=", &arg)) {
		options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
		ALLOC_GROW(options->anchors, options->anchors_nr + 1,
			   options->anchors_alloc);
		options->anchors[options->anchors_nr++] = xstrdup(arg);
4938
	}
4939 4940 4941

	/* flags options */
	else if (!strcmp(arg, "--binary")) {
4942
		enable_patch_output(&options->output_format);
4943
		options->flags.binary = 1;
4944 4945
	}
	else if (!strcmp(arg, "--full-index"))
4946
		options->flags.full_index = 1;
4947
	else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
4948
		options->flags.text = 1;
4949
	else if (!strcmp(arg, "-R"))
4950
		options->flags.reverse_diff = 1;
J
Junio C Hamano 已提交
4951
	else if (!strcmp(arg, "--find-copies-harder"))
4952
		options->flags.find_copies_harder = 1;
4953
	else if (!strcmp(arg, "--follow"))
4954
		options->flags.follow_renames = 1;
4955
	else if (!strcmp(arg, "--no-follow")) {
4956 4957
		options->flags.follow_renames = 0;
		options->flags.default_follow_renames = 0;
4958
	} else if (skip_to_optional_arg_default(arg, "--color", &arg, "always")) {
4959
		int value = git_config_colorbool(NULL, arg);
4960
		if (value < 0)
4961
			return error("option `color' expects \"always\", \"auto\", or \"never\"");
4962
		options->use_color = value;
4963
	}
4964
	else if (!strcmp(arg, "--no-color"))
4965
		options->use_color = 0;
4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977
	else if (!strcmp(arg, "--color-moved")) {
		if (diff_color_moved_default)
			options->color_moved = diff_color_moved_default;
		if (options->color_moved == COLOR_MOVED_NO)
			options->color_moved = COLOR_MOVED_DEFAULT;
	} else if (!strcmp(arg, "--no-color-moved"))
		options->color_moved = COLOR_MOVED_NO;
	else if (skip_prefix(arg, "--color-moved=", &arg)) {
		int cm = parse_color_moved(arg);
		if (cm < 0)
			die("bad --color-moved argument: %s", arg);
		options->color_moved = cm;
4978 4979
	} else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
		options->color_moved_ws_handling = parse_color_moved_ws(arg);
4980
	} else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
4981
		options->use_color = 1;
4982
		options->word_diff = DIFF_WORDS_COLOR;
4983
	}
4984 4985 4986 4987
	else if (!strcmp(arg, "--word-diff")) {
		if (options->word_diff == DIFF_WORDS_NONE)
			options->word_diff = DIFF_WORDS_PLAIN;
	}
4988 4989
	else if (skip_prefix(arg, "--word-diff=", &arg)) {
		if (!strcmp(arg, "plain"))
4990
			options->word_diff = DIFF_WORDS_PLAIN;
4991
		else if (!strcmp(arg, "color")) {
4992
			options->use_color = 1;
4993 4994
			options->word_diff = DIFF_WORDS_COLOR;
		}
4995
		else if (!strcmp(arg, "porcelain"))
4996
			options->word_diff = DIFF_WORDS_PORCELAIN;
4997
		else if (!strcmp(arg, "none"))
4998 4999
			options->word_diff = DIFF_WORDS_NONE;
		else
5000
			die("bad --word-diff argument: %s", arg);
5001
	}
5002
	else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
5003 5004
		if (options->word_diff == DIFF_WORDS_NONE)
			options->word_diff = DIFF_WORDS_PLAIN;
5005 5006
		options->word_regex = optarg;
		return argcount;
5007
	}
5008
	else if (!strcmp(arg, "--exit-code"))
5009
		options->flags.exit_with_status = 1;
J
Junio C Hamano 已提交
5010
	else if (!strcmp(arg, "--quiet"))
5011
		options->flags.quick = 1;
J
Johannes Schindelin 已提交
5012
	else if (!strcmp(arg, "--ext-diff"))
5013
		options->flags.allow_external = 1;
J
Johannes Schindelin 已提交
5014
	else if (!strcmp(arg, "--no-ext-diff"))
5015
		options->flags.allow_external = 0;
5016
	else if (!strcmp(arg, "--textconv")) {
5017 5018
		options->flags.allow_textconv = 1;
		options->flags.textconv_set_via_cmdline = 1;
5019
	} else if (!strcmp(arg, "--no-textconv"))
5020
		options->flags.allow_textconv = 0;
5021
	else if (skip_to_optional_arg_default(arg, "--ignore-submodules", &arg, "all")) {
5022
		options->flags.override_submodule_config = 1;
5023
		handle_ignore_submodules_arg(options, arg);
5024
	} else if (skip_to_optional_arg_default(arg, "--submodule", &arg, "log"))
5025
		return parse_submodule_opt(options, arg);
5026
	else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
5027
		return parse_ws_error_highlight_opt(options, arg);
5028 5029 5030 5031
	else if (!strcmp(arg, "--ita-invisible-in-index"))
		options->ita_invisible_in_index = 1;
	else if (!strcmp(arg, "--ita-visible-in-index"))
		options->ita_invisible_in_index = 0;
5032 5033 5034 5035

	/* misc options */
	else if (!strcmp(arg, "-z"))
		options->line_termination = 0;
5036 5037 5038 5039 5040 5041
	else if ((argcount = short_opt('l', av, &optarg))) {
		options->rename_limit = strtoul(optarg, NULL, 10);
		return argcount;
	}
	else if ((argcount = short_opt('S', av, &optarg))) {
		options->pickaxe = optarg;
5042 5043 5044 5045 5046
		options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
		return argcount;
	} else if ((argcount = short_opt('G', av, &optarg))) {
		options->pickaxe = optarg;
		options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
5047 5048
		return argcount;
	}
5049
	else if (!strcmp(arg, "--pickaxe-all"))
5050
		options->pickaxe_opts |= DIFF_PICKAXE_ALL;
5051
	else if (!strcmp(arg, "--pickaxe-regex"))
5052
		options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
5053
	else if ((argcount = short_opt('O', av, &optarg))) {
5054
		options->orderfile = prefix_filename(prefix, optarg);
5055
		return argcount;
5056 5057
	} else if (skip_prefix(arg, "--find-object=", &arg))
		return parse_objfind_opt(options, arg);
5058
	else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
5059 5060 5061 5062
		int offending = parse_diff_filter_opt(optarg, options);
		if (offending)
			die("unknown change class '%c' in --diff-filter=%s",
			    offending, optarg);
5063 5064
		return argcount;
	}
5065 5066
	else if (!strcmp(arg, "--no-abbrev"))
		options->abbrev = 0;
5067 5068
	else if (!strcmp(arg, "--abbrev"))
		options->abbrev = DEFAULT_ABBREV;
5069 5070
	else if (skip_prefix(arg, "--abbrev=", &arg)) {
		options->abbrev = strtoul(arg, NULL, 10);
5071 5072
		if (options->abbrev < MINIMUM_ABBREV)
			options->abbrev = MINIMUM_ABBREV;
5073 5074
		else if (the_hash_algo->hexsz < options->abbrev)
			options->abbrev = the_hash_algo->hexsz;
5075
	}
5076 5077 5078 5079
	else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
		options->a_prefix = optarg;
		return argcount;
	}
5080 5081 5082 5083 5084 5085
	else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) {
		options->line_prefix = optarg;
		options->line_prefix_length = strlen(options->line_prefix);
		graph_setup_line_prefix(options);
		return argcount;
	}
5086 5087 5088 5089
	else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
		options->b_prefix = optarg;
		return argcount;
	}
5090 5091
	else if (!strcmp(arg, "--no-prefix"))
		options->a_prefix = options->b_prefix = "";
5092 5093 5094
	else if (opt_arg(arg, '\0', "inter-hunk-context",
			 &options->interhunkcontext))
		;
5095
	else if (!strcmp(arg, "-W"))
5096
		options->flags.funccontext = 1;
5097
	else if (!strcmp(arg, "--function-context"))
5098
		options->flags.funccontext = 1;
5099
	else if (!strcmp(arg, "--no-function-context"))
5100
		options->flags.funccontext = 0;
5101
	else if ((argcount = parse_long_opt("output", av, &optarg))) {
5102
		char *path = prefix_filename(prefix, optarg);
5103
		options->file = xfopen(path, "w");
5104
		options->close_file = 1;
5105 5106
		if (options->use_color != GIT_COLOR_ALWAYS)
			options->use_color = GIT_COLOR_NEVER;
5107
		free(path);
5108
		return argcount;
5109
	} else
J
Junio C Hamano 已提交
5110 5111 5112 5113
		return 0;
	return 1;
}

5114
int parse_rename_score(const char **cp_p)
J
Junio C Hamano 已提交
5115 5116 5117 5118 5119 5120 5121 5122
{
	unsigned long num, scale;
	int ch, dot;
	const char *cp = *cp_p;

	num = 0;
	scale = 1;
	dot = 0;
5123
	for (;;) {
J
Junio C Hamano 已提交
5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146
		ch = *cp;
		if ( !dot && ch == '.' ) {
			scale = 1;
			dot = 1;
		} else if ( ch == '%' ) {
			scale = dot ? scale*100 : 100;
			cp++;	/* % is always at the end */
			break;
		} else if ( ch >= '0' && ch <= '9' ) {
			if ( scale < 100000 ) {
				scale *= 10;
				num = (num*10) + (ch-'0');
			}
		} else {
			break;
		}
		cp++;
	}
	*cp_p = cp;

	/* user says num divided by scale and we say internally that
	 * is MAX_SCORE * num / scale.
	 */
5147
	return (int)((num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale));
J
Junio C Hamano 已提交
5148 5149
}

J
Junio C Hamano 已提交
5150
static int diff_scoreopt_parse(const char *opt)
J
Junio C Hamano 已提交
5151 5152 5153 5154 5155 5156
{
	int opt1, opt2, cmd;

	if (*opt++ != '-')
		return -1;
	cmd = *opt++;
5157 5158
	if (cmd == '-') {
		/* convert the long-form arguments into short-form versions */
5159
		if (skip_prefix(opt, "break-rewrites", &opt)) {
5160 5161
			if (*opt == 0 || *opt++ == '=')
				cmd = 'B';
5162
		} else if (skip_prefix(opt, "find-copies", &opt)) {
5163 5164
			if (*opt == 0 || *opt++ == '=')
				cmd = 'C';
5165
		} else if (skip_prefix(opt, "find-renames", &opt)) {
5166 5167 5168 5169
			if (*opt == 0 || *opt++ == '=')
				cmd = 'M';
		}
	}
J
Junio C Hamano 已提交
5170
	if (cmd != 'M' && cmd != 'C' && cmd != 'B')
J
Justin Lebar 已提交
5171
		return -1; /* that is not a -M, -C, or -B option */
J
Junio C Hamano 已提交
5172

5173
	opt1 = parse_rename_score(&opt);
J
Junio C Hamano 已提交
5174 5175 5176 5177 5178 5179 5180 5181 5182
	if (cmd != 'B')
		opt2 = 0;
	else {
		if (*opt == 0)
			opt2 = 0;
		else if (*opt != '/')
			return -1; /* we expect -B80/99 or -B80 */
		else {
			opt++;
5183
			opt2 = parse_rename_score(&opt);
J
Junio C Hamano 已提交
5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194
		}
	}
	if (*opt != 0)
		return -1;
	return opt1 | (opt2 << 16);
}

struct diff_queue_struct diff_queued_diff;

void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
{
D
Dmitry S. Dolzhenko 已提交
5195
	ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc);
J
Junio C Hamano 已提交
5196 5197 5198 5199 5200 5201 5202
	queue->queue[queue->nr++] = dp;
}

struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
				 struct diff_filespec *one,
				 struct diff_filespec *two)
{
5203
	struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
J
Junio C Hamano 已提交
5204 5205 5206 5207 5208 5209 5210 5211 5212
	dp->one = one;
	dp->two = two;
	if (queue)
		diff_q(queue, dp);
	return dp;
}

void diff_free_filepair(struct diff_filepair *p)
{
5213 5214
	free_filespec(p->one);
	free_filespec(p->two);
J
Junio C Hamano 已提交
5215 5216 5217
	free(p);
}

J
Jeff King 已提交
5218
const char *diff_aligned_abbrev(const struct object_id *oid, int len)
J
Junio C Hamano 已提交
5219 5220 5221 5222
{
	int abblen;
	const char *abbrev;

5223
	/* Do we want all 40 hex characters? */
5224
	if (len == the_hash_algo->hexsz)
J
Jeff King 已提交
5225 5226
		return oid_to_hex(oid);

5227
	/* An abbreviated value is fine, possibly followed by an ellipsis. */
5228
	abbrev = diff_abbrev_oid(oid, len);
5229 5230 5231 5232

	if (!print_sha1_ellipsis())
		return abbrev;

J
Junio C Hamano 已提交
5233
	abblen = strlen(abbrev);
5234 5235

	/*
5236
	 * In well-behaved cases, where the abbreviated result is the
5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253
	 * same as the requested length, append three dots after the
	 * abbreviation (hence the whole logic is limited to the case
	 * where abblen < 37); when the actual abbreviated result is a
	 * bit longer than the requested length, we reduce the number
	 * of dots so that they match the well-behaved ones.  However,
	 * if the actual abbreviation is longer than the requested
	 * length by more than three, we give up on aligning, and add
	 * three dots anyway, to indicate that the output is not the
	 * full object name.  Yes, this may be suboptimal, but this
	 * appears only in "diff --raw --abbrev" output and it is not
	 * worth the effort to change it now.  Note that this would
	 * likely to work fine when the automatic sizing of default
	 * abbreviation length is used--we would be fed -1 in "len" in
	 * that case, and will end up always appending three-dots, but
	 * the automatic sizing is supposed to give abblen that ensures
	 * uniqueness across all objects (statistically speaking).
	 */
5254
	if (abblen < the_hash_algo->hexsz - 3) {
5255
		static char hex[GIT_MAX_HEXSZ + 1];
J
Junio C Hamano 已提交
5256
		if (len < abblen && abblen <= len + 2)
5257
			xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
J
Junio C Hamano 已提交
5258
		else
5259
			xsnprintf(hex, sizeof(hex), "%s...", abbrev);
J
Junio C Hamano 已提交
5260 5261
		return hex;
	}
J
Jeff King 已提交
5262 5263

	return oid_to_hex(oid);
J
Junio C Hamano 已提交
5264 5265
}

5266
static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
J
Junio C Hamano 已提交
5267
{
5268 5269
	int line_termination = opt->line_termination;
	int inter_name_termination = line_termination ? '\t' : '\0';
J
Junio C Hamano 已提交
5270

5271
	fprintf(opt->file, "%s", diff_line_prefix(opt));
5272
	if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
5273
		fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
J
Jeff King 已提交
5274
			diff_aligned_abbrev(&p->one->oid, opt->abbrev));
5275
		fprintf(opt->file, "%s ",
J
Jeff King 已提交
5276
			diff_aligned_abbrev(&p->two->oid, opt->abbrev));
J
Junio C Hamano 已提交
5277
	}
5278
	if (p->score) {
5279 5280
		fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
			inter_name_termination);
5281
	} else {
5282
		fprintf(opt->file, "%c%c", p->status, inter_name_termination);
J
Junio C Hamano 已提交
5283 5284
	}

5285 5286 5287 5288 5289 5290
	if (p->status == DIFF_STATUS_COPIED ||
	    p->status == DIFF_STATUS_RENAMED) {
		const char *name_a, *name_b;
		name_a = p->one->path;
		name_b = p->two->path;
		strip_prefix(opt->prefix_length, &name_a, &name_b);
5291 5292
		write_name_quoted(name_a, opt->file, inter_name_termination);
		write_name_quoted(name_b, opt->file, line_termination);
5293
	} else {
5294 5295 5296 5297
		const char *name_a, *name_b;
		name_a = p->one->mode ? p->one->path : p->two->path;
		name_b = NULL;
		strip_prefix(opt->prefix_length, &name_a, &name_b);
5298
		write_name_quoted(name_a, opt->file, line_termination);
5299
	}
J
Junio C Hamano 已提交
5300 5301 5302 5303 5304 5305 5306 5307 5308
}

int diff_unmodified_pair(struct diff_filepair *p)
{
	/* This function is written stricter than necessary to support
	 * the currently implemented transformers, but the idea is to
	 * let transformers to produce diff_filepairs any way they want,
	 * and filter and clean them up here before producing the output.
	 */
5309
	struct diff_filespec *one = p->one, *two = p->two;
J
Junio C Hamano 已提交
5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324

	if (DIFF_PAIR_UNMERGED(p))
		return 0; /* unmerged is interesting */

	/* deletion, addition, mode or type change
	 * and rename are all interesting.
	 */
	if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
	    DIFF_PAIR_MODE_CHANGED(p) ||
	    strcmp(one->path, two->path))
		return 0;

	/* both are valid and point at the same path.  that is, we are
	 * dealing with a change.
	 */
5325
	if (one->oid_valid && two->oid_valid &&
J
Jeff King 已提交
5326
	    oideq(&one->oid, &two->oid) &&
5327
	    !one->dirty_submodule && !two->dirty_submodule)
J
Junio C Hamano 已提交
5328
		return 1; /* no change */
5329
	if (!one->oid_valid && !two->oid_valid)
J
Junio C Hamano 已提交
5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353
		return 1; /* both look at the same file on the filesystem. */
	return 0;
}

static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
{
	if (diff_unmodified_pair(p))
		return;

	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
		return; /* no tree diffs in patch format */

	run_diff(p, o);
}

static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
			    struct diffstat_t *diffstat)
{
	if (diff_unmodified_pair(p))
		return;

	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
5354
		return; /* no useful stat for tree diffs */
J
Junio C Hamano 已提交
5355 5356 5357 5358

	run_diffstat(p, o, diffstat);
}

5359 5360 5361 5362 5363 5364 5365 5366
static void diff_flush_checkdiff(struct diff_filepair *p,
		struct diff_options *o)
{
	if (diff_unmodified_pair(p))
		return;

	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
5367
		return; /* nothing to check in tree diffs */
5368 5369 5370 5371

	run_checkdiff(p, o);
}

J
Junio C Hamano 已提交
5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389
int diff_queue_is_empty(void)
{
	struct diff_queue_struct *q = &diff_queued_diff;
	int i;
	for (i = 0; i < q->nr; i++)
		if (!diff_unmodified_pair(q->queue[i]))
			return 0;
	return 1;
}

#if DIFF_DEBUG
void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
{
	fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
		x, one ? one : "",
		s->path,
		DIFF_FILE_VALID(s) ? "valid" : "invalid",
		s->mode,
5390
		s->oid_valid ? oid_to_hex(&s->oid) : "");
J
Jeff King 已提交
5391
	fprintf(stderr, "queue[%d] %s size %lu\n",
J
Junio C Hamano 已提交
5392
		x, one ? one : "",
J
Jeff King 已提交
5393
		s->size);
J
Junio C Hamano 已提交
5394 5395 5396 5397 5398 5399
}

void diff_debug_filepair(const struct diff_filepair *p, int i)
{
	diff_debug_filespec(p->one, i, "one");
	diff_debug_filespec(p->two, i, "two");
5400
	fprintf(stderr, "score %d, status %c rename_used %d broken %d\n",
J
Junio C Hamano 已提交
5401
		p->score, p->status ? p->status : '?',
5402
		p->one->rename_used, p->broken_pair);
J
Junio C Hamano 已提交
5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419
}

void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
{
	int i;
	if (msg)
		fprintf(stderr, "%s\n", msg);
	fprintf(stderr, "q->nr = %d\n", q->nr);
	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];
		diff_debug_filepair(p, i);
	}
}
#endif

static void diff_resolve_rename_copy(void)
{
5420 5421
	int i;
	struct diff_filepair *p;
J
Junio C Hamano 已提交
5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442
	struct diff_queue_struct *q = &diff_queued_diff;

	diff_debug_queue("resolve-rename-copy", q);

	for (i = 0; i < q->nr; i++) {
		p = q->queue[i];
		p->status = 0; /* undecided */
		if (DIFF_PAIR_UNMERGED(p))
			p->status = DIFF_STATUS_UNMERGED;
		else if (!DIFF_FILE_VALID(p->one))
			p->status = DIFF_STATUS_ADDED;
		else if (!DIFF_FILE_VALID(p->two))
			p->status = DIFF_STATUS_DELETED;
		else if (DIFF_PAIR_TYPE_CHANGED(p))
			p->status = DIFF_STATUS_TYPE_CHANGED;

		/* from this point on, we are dealing with a pair
		 * whose both sides are valid and of the same type, i.e.
		 * either in-place edit or rename/copy edit.
		 */
		else if (DIFF_PAIR_RENAME(p)) {
5443 5444 5445 5446 5447 5448 5449 5450 5451
			/*
			 * A rename might have re-connected a broken
			 * pair up, causing the pathnames to be the
			 * same again. If so, that's not a rename at
			 * all, just a modification..
			 *
			 * Otherwise, see if this source was used for
			 * multiple renames, in which case we decrement
			 * the count, and call it a copy.
J
Junio C Hamano 已提交
5452
			 */
5453 5454 5455
			if (!strcmp(p->one->path, p->two->path))
				p->status = DIFF_STATUS_MODIFIED;
			else if (--p->one->rename_used > 0)
J
Junio C Hamano 已提交
5456
				p->status = DIFF_STATUS_COPIED;
5457
			else
J
Junio C Hamano 已提交
5458 5459
				p->status = DIFF_STATUS_RENAMED;
		}
5460
		else if (!oideq(&p->one->oid, &p->two->oid) ||
5461
			 p->one->mode != p->two->mode ||
5462 5463
			 p->one->dirty_submodule ||
			 p->two->dirty_submodule ||
5464
			 is_null_oid(&p->one->oid))
J
Junio C Hamano 已提交
5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477
			p->status = DIFF_STATUS_MODIFIED;
		else {
			/* This is a "no-change" entry and should not
			 * happen anymore, but prepare for broken callers.
			 */
			error("feeding unmodified %s to diffcore",
			      p->one->path);
			p->status = DIFF_STATUS_UNKNOWN;
		}
	}
	diff_debug_queue("resolve-rename-copy done", q);
}

5478
static int check_pair_status(struct diff_filepair *p)
J
Junio C Hamano 已提交
5479 5480 5481
{
	switch (p->status) {
	case DIFF_STATUS_UNKNOWN:
5482
		return 0;
J
Junio C Hamano 已提交
5483 5484 5485
	case 0:
		die("internal error in diff-resolve-rename-copy");
	default:
5486
		return 1;
J
Junio C Hamano 已提交
5487 5488 5489
	}
}

5490 5491 5492 5493 5494 5495 5496 5497
static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
{
	int fmt = opt->output_format;

	if (fmt & DIFF_FORMAT_CHECKDIFF)
		diff_flush_checkdiff(p, opt);
	else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
		diff_flush_raw(p, opt);
5498 5499 5500 5501 5502
	else if (fmt & DIFF_FORMAT_NAME) {
		const char *name_a, *name_b;
		name_a = p->two->path;
		name_b = NULL;
		strip_prefix(opt->prefix_length, &name_a, &name_b);
5503
		fprintf(opt->file, "%s", diff_line_prefix(opt));
5504
		write_name_quoted(name_a, opt->file, opt->line_termination);
5505
	}
5506 5507
}

5508
static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
S
Sean 已提交
5509
{
5510
	struct strbuf sb = STRBUF_INIT;
S
Sean 已提交
5511
	if (fs->mode)
5512
		strbuf_addf(&sb, " %s mode %06o ", newdelete, fs->mode);
S
Sean 已提交
5513
	else
5514
		strbuf_addf(&sb, " %s ", newdelete);
S
Sean 已提交
5515

5516 5517 5518 5519 5520 5521
	quote_c_style(fs->path, &sb, NULL, 0);
	strbuf_addch(&sb, '\n');
	emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
			 sb.buf, sb.len, 0);
	strbuf_release(&sb);
}
S
Sean 已提交
5522

5523 5524
static void show_mode_change(struct diff_options *opt, struct diff_filepair *p,
		int show_name)
S
Sean 已提交
5525 5526
{
	if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
5527 5528 5529
		struct strbuf sb = STRBUF_INIT;
		strbuf_addf(&sb, " mode change %06o => %06o",
			    p->one->mode, p->two->mode);
5530
		if (show_name) {
5531 5532
			strbuf_addch(&sb, ' ');
			quote_c_style(p->two->path, &sb, NULL, 0);
5533
		}
5534
		strbuf_addch(&sb, '\n');
5535 5536 5537
		emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
				 sb.buf, sb.len, 0);
		strbuf_release(&sb);
S
Sean 已提交
5538 5539 5540
	}
}

5541 5542
static void show_rename_copy(struct diff_options *opt, const char *renamecopy,
		struct diff_filepair *p)
S
Sean 已提交
5543
{
5544
	struct strbuf sb = STRBUF_INIT;
5545 5546 5547
	struct strbuf names = STRBUF_INIT;

	pprint_rename(&names, p->one->path, p->two->path);
5548
	strbuf_addf(&sb, " %s %s (%d%%)\n",
5549 5550
		    renamecopy, names.buf, similarity_index(p));
	strbuf_release(&names);
5551 5552 5553
	emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
				 sb.buf, sb.len, 0);
	show_mode_change(opt, p, 0);
5554
	strbuf_release(&sb);
S
Sean 已提交
5555 5556
}

5557
static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
S
Sean 已提交
5558 5559 5560
{
	switch(p->status) {
	case DIFF_STATUS_DELETED:
5561
		show_file_mode_name(opt, "delete", p->one);
S
Sean 已提交
5562 5563
		break;
	case DIFF_STATUS_ADDED:
5564
		show_file_mode_name(opt, "create", p->two);
S
Sean 已提交
5565 5566
		break;
	case DIFF_STATUS_COPIED:
5567
		show_rename_copy(opt, "copy", p);
S
Sean 已提交
5568 5569
		break;
	case DIFF_STATUS_RENAMED:
5570
		show_rename_copy(opt, "rename", p);
S
Sean 已提交
5571 5572 5573
		break;
	default:
		if (p->score) {
5574 5575 5576 5577 5578 5579
			struct strbuf sb = STRBUF_INIT;
			strbuf_addstr(&sb, " rewrite ");
			quote_c_style(p->two->path, &sb, NULL, 0);
			strbuf_addf(&sb, " (%d%%)\n", similarity_index(p));
			emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
					 sb.buf, sb.len, 0);
5580
			strbuf_release(&sb);
5581
		}
5582
		show_mode_change(opt, p, !p->score);
S
Sean 已提交
5583 5584 5585 5586
		break;
	}
}

5587
struct patch_id_t {
5588
	git_SHA_CTX *ctx;
5589 5590 5591 5592 5593 5594
	int patchlen;
};

static int remove_space(char *line, int len)
{
	int i;
5595 5596
	char *dst = line;
	unsigned char c;
5597

5598 5599 5600
	for (i = 0; i < len; i++)
		if (!isspace((c = line[i])))
			*dst++ = c;
5601

5602
	return dst - line;
5603 5604 5605 5606 5607 5608 5609 5610
}

static void patch_id_consume(void *priv, char *line, unsigned long len)
{
	struct patch_id_t *data = priv;
	int new_len;

	/* Ignore line numbers when computing the SHA1 of the patch */
5611
	if (starts_with(line, "@@ -"))
5612 5613 5614 5615
		return;

	new_len = remove_space(line, len);

5616
	git_SHA1_Update(data->ctx, line, new_len);
5617 5618 5619
	data->patchlen += new_len;
}

5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632
static void patch_id_add_string(git_SHA_CTX *ctx, const char *str)
{
	git_SHA1_Update(ctx, str, strlen(str));
}

static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
{
	/* large enough for 2^32 in octal */
	char buf[12];
	int len = xsnprintf(buf, sizeof(buf), "%06o", mode);
	git_SHA1_Update(ctx, buf, len);
}

5633
/* returns 0 upon success, and writes result into sha1 */
5634
static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
5635 5636 5637
{
	struct diff_queue_struct *q = &diff_queued_diff;
	int i;
5638
	git_SHA_CTX ctx;
5639 5640
	struct patch_id_t data;

5641
	git_SHA1_Init(&ctx);
5642 5643 5644 5645 5646 5647 5648 5649 5650 5651
	memset(&data, 0, sizeof(struct patch_id_t));
	data.ctx = &ctx;

	for (i = 0; i < q->nr; i++) {
		xpparam_t xpp;
		xdemitconf_t xecfg;
		mmfile_t mf1, mf2;
		struct diff_filepair *p = q->queue[i];
		int len1, len2;

B
Brian Downing 已提交
5652
		memset(&xpp, 0, sizeof(xpp));
5653
		memset(&xecfg, 0, sizeof(xecfg));
5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665
		if (p->status == 0)
			return error("internal diff status error");
		if (p->status == DIFF_STATUS_UNKNOWN)
			continue;
		if (diff_unmodified_pair(p))
			continue;
		if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
		    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
			continue;
		if (DIFF_PAIR_UNMERGED(p))
			continue;

5666 5667
		diff_fill_oid_info(p->one);
		diff_fill_oid_info(p->two);
5668 5669 5670

		len1 = remove_space(p->one->path, strlen(p->one->path));
		len2 = remove_space(p->two->path, strlen(p->two->path));
5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694
		patch_id_add_string(&ctx, "diff--git");
		patch_id_add_string(&ctx, "a/");
		git_SHA1_Update(&ctx, p->one->path, len1);
		patch_id_add_string(&ctx, "b/");
		git_SHA1_Update(&ctx, p->two->path, len2);

		if (p->one->mode == 0) {
			patch_id_add_string(&ctx, "newfilemode");
			patch_id_add_mode(&ctx, p->two->mode);
			patch_id_add_string(&ctx, "---/dev/null");
			patch_id_add_string(&ctx, "+++b/");
			git_SHA1_Update(&ctx, p->two->path, len2);
		} else if (p->two->mode == 0) {
			patch_id_add_string(&ctx, "deletedfilemode");
			patch_id_add_mode(&ctx, p->one->mode);
			patch_id_add_string(&ctx, "---a/");
			git_SHA1_Update(&ctx, p->one->path, len1);
			patch_id_add_string(&ctx, "+++/dev/null");
		} else {
			patch_id_add_string(&ctx, "---a/");
			git_SHA1_Update(&ctx, p->one->path, len1);
			patch_id_add_string(&ctx, "+++b/");
			git_SHA1_Update(&ctx, p->two->path, len2);
		}
5695

5696 5697 5698 5699 5700 5701 5702
		if (diff_header_only)
			continue;

		if (fill_mmfile(&mf1, p->one) < 0 ||
		    fill_mmfile(&mf2, p->two) < 0)
			return error("unable to read files to diff");

5703 5704
		if (diff_filespec_is_binary(p->one) ||
		    diff_filespec_is_binary(p->two)) {
5705
			git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
5706
					GIT_SHA1_HEXSZ);
5707
			git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
5708
					GIT_SHA1_HEXSZ);
5709 5710 5711
			continue;
		}

R
René Scharfe 已提交
5712
		xpp.flags = 0;
5713
		xecfg.ctxlen = 3;
5714
		xecfg.flags = 0;
J
Jeff King 已提交
5715 5716 5717 5718
		if (xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
				  &xpp, &xecfg))
			return error("unable to generate patch-id diff for %s",
				     p->one->path);
5719 5720
	}

5721
	git_SHA1_Final(oid->hash, &ctx);
5722 5723 5724
	return 0;
}

5725
int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
5726 5727 5728
{
	struct diff_queue_struct *q = &diff_queued_diff;
	int i;
5729
	int result = diff_get_patch_id(options, oid, diff_header_only);
5730 5731 5732 5733 5734

	for (i = 0; i < q->nr; i++)
		diff_free_filepair(q->queue[i]);

	free(q->queue);
B
Bo Yang 已提交
5735
	DIFF_QUEUE_CLEAR(q);
5736 5737 5738 5739

	return result;
}

5740
static int is_summary_empty(const struct diff_queue_struct *q)
J
Junio C Hamano 已提交
5741 5742 5743
{
	int i;

5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760
	for (i = 0; i < q->nr; i++) {
		const struct diff_filepair *p = q->queue[i];

		switch (p->status) {
		case DIFF_STATUS_DELETED:
		case DIFF_STATUS_ADDED:
		case DIFF_STATUS_COPIED:
		case DIFF_STATUS_RENAMED:
			return 0;
		default:
			if (p->score)
				return 0;
			if (p->one->mode && p->two->mode &&
			    p->one->mode != p->two->mode)
				return 0;
			break;
		}
J
Junio C Hamano 已提交
5761
	}
5762 5763 5764
	return 1;
}

5765
static const char rename_limit_warning[] =
5766
N_("inexact rename detection was skipped due to too many files.");
5767 5768

static const char degrade_cc_to_c_warning[] =
5769
N_("only found copies from modified paths due to too many files.");
5770 5771

static const char rename_limit_advice[] =
5772 5773
N_("you may want to set your %s variable to at least "
   "%d and retry the command.");
5774 5775 5776

void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
{
5777
	fflush(stdout);
5778
	if (degraded_cc)
5779
		warning(_(degrade_cc_to_c_warning));
5780
	else if (needed)
5781
		warning(_(rename_limit_warning));
5782 5783
	else
		return;
5784
	if (0 < needed)
5785
		warning(_(rename_limit_advice), varname, needed);
5786 5787
}

5788 5789 5790
static void diff_flush_patch_all_file_pairs(struct diff_options *o)
{
	int i;
5791
	static struct emitted_diff_symbols esm = EMITTED_DIFF_SYMBOLS_INIT;
5792
	struct diff_queue_struct *q = &diff_queued_diff;
5793 5794

	if (WSEH_NEW & WS_RULE_MASK)
5795
		BUG("WS rules bit mask overlaps with diff symbol flags");
5796

5797 5798
	if (o->color_moved)
		o->emitted_symbols = &esm;
5799

5800 5801 5802 5803 5804
	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];
		if (check_pair_status(p))
			diff_flush_patch(p, o);
	}
5805 5806

	if (o->emitted_symbols) {
5807 5808 5809
		if (o->color_moved) {
			struct hashmap add_lines, del_lines;

5810 5811 5812 5813
			if (o->color_moved_ws_handling &
			    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
				o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;

5814 5815
			hashmap_init(&del_lines, moved_entry_cmp, o, 0);
			hashmap_init(&add_lines, moved_entry_cmp, o, 0);
5816 5817 5818

			add_lines_to_move_detection(o, &add_lines, &del_lines);
			mark_color_as_moved(o, &add_lines, &del_lines);
5819 5820
			if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
				dim_moved_lines(o);
5821 5822 5823 5824 5825

			hashmap_free(&add_lines, 0);
			hashmap_free(&del_lines, 0);
		}

5826 5827 5828 5829 5830 5831 5832
		for (i = 0; i < esm.nr; i++)
			emit_diff_symbol_from_struct(o, &esm.buf[i]);

		for (i = 0; i < esm.nr; i++)
			free((void *)esm.buf[i].line);
	}
	esm.nr = 0;
5833 5834
}

J
Junio C Hamano 已提交
5835 5836 5837
void diff_flush(struct diff_options *options)
{
	struct diff_queue_struct *q = &diff_queued_diff;
5838
	int i, output_format = options->output_format;
5839
	int separator = 0;
5840
	int dirstat_by_line = 0;
J
Junio C Hamano 已提交
5841

5842 5843 5844 5845
	/*
	 * Order: raw, stat, summary, patch
	 * or:    name/name-status/checkdiff (other bits clear)
	 */
5846 5847
	if (!q->nr)
		goto free_queue;
J
Junio C Hamano 已提交
5848

5849 5850 5851 5852
	if (output_format & (DIFF_FORMAT_RAW |
			     DIFF_FORMAT_NAME |
			     DIFF_FORMAT_NAME_STATUS |
			     DIFF_FORMAT_CHECKDIFF)) {
J
Junio C Hamano 已提交
5853 5854
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
5855 5856
			if (check_pair_status(p))
				flush_one_pair(p, options);
J
Junio C Hamano 已提交
5857
		}
5858
		separator++;
J
Junio C Hamano 已提交
5859
	}
5860

5861
	if (output_format & DIFF_FORMAT_DIRSTAT && options->flags.dirstat_by_line)
5862 5863 5864 5865
		dirstat_by_line = 1;

	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
	    dirstat_by_line) {
5866
		struct diffstat_t diffstat;
5867

5868
		memset(&diffstat, 0, sizeof(struct diffstat_t));
J
Junio C Hamano 已提交
5869 5870
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
5871
			if (check_pair_status(p))
5872
				diff_flush_stat(p, options, &diffstat);
J
Junio C Hamano 已提交
5873
		}
J
Junio C Hamano 已提交
5874 5875 5876 5877
		if (output_format & DIFF_FORMAT_NUMSTAT)
			show_numstat(&diffstat, options);
		if (output_format & DIFF_FORMAT_DIFFSTAT)
			show_stats(&diffstat, options);
5878
		if (output_format & DIFF_FORMAT_SHORTSTAT)
5879
			show_shortstats(&diffstat, options);
5880
		if (output_format & DIFF_FORMAT_DIRSTAT && dirstat_by_line)
5881
			show_dirstat_by_line(&diffstat, options);
5882
		free_diffstat_info(&diffstat);
5883
		separator++;
J
Junio C Hamano 已提交
5884
	}
5885
	if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)
5886
		show_dirstat(options);
J
Junio C Hamano 已提交
5887

5888
	if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
5889 5890 5891
		for (i = 0; i < q->nr; i++) {
			diff_summary(options, q->queue[i]);
		}
5892
		separator++;
J
Junio C Hamano 已提交
5893 5894
	}

5895
	if (output_format & DIFF_FORMAT_NO_OUTPUT &&
5896 5897
	    options->flags.exit_with_status &&
	    options->flags.diff_from_contents) {
5898 5899
		/*
		 * run diff_flush_patch for the exit status. setting
O
Ondřej Bílka 已提交
5900
		 * options->file to /dev/null should be safe, because we
5901 5902 5903 5904
		 * aren't supposed to produce any output anyway.
		 */
		if (options->close_file)
			fclose(options->file);
5905
		options->file = xfopen("/dev/null", "w");
5906
		options->close_file = 1;
5907
		options->color_moved = 0;
5908 5909 5910 5911 5912 5913 5914 5915 5916
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (check_pair_status(p))
				diff_flush_patch(p, options);
			if (options->found_changes)
				break;
		}
	}

5917
	if (output_format & DIFF_FORMAT_PATCH) {
5918
		if (separator) {
5919
			emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
5920
			if (options->stat_sep)
5921
				/* attach patch instead of inline */
5922 5923
				emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
						 NULL, 0, 0);
5924 5925
		}

5926
		diff_flush_patch_all_file_pairs(options);
S
Sean 已提交
5927 5928
	}

5929 5930 5931
	if (output_format & DIFF_FORMAT_CALLBACK)
		options->format_callback(q, options, options->format_callback_data);

5932 5933
	for (i = 0; i < q->nr; i++)
		diff_free_filepair(q->queue[i]);
5934
free_queue:
J
Junio C Hamano 已提交
5935
	free(q->queue);
B
Bo Yang 已提交
5936
	DIFF_QUEUE_CLEAR(q);
5937 5938
	if (options->close_file)
		fclose(options->file);
5939 5940

	/*
J
Jim Meyering 已提交
5941
	 * Report the content-level differences with HAS_CHANGES;
5942 5943 5944
	 * diff_addremove/diff_change does not set the bit when
	 * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
	 */
5945
	if (options->flags.diff_from_contents) {
5946
		if (options->found_changes)
5947
			options->flags.has_changes = 1;
5948
		else
5949
			options->flags.has_changes = 0;
5950
	}
J
Junio C Hamano 已提交
5951 5952
}

J
Junio C Hamano 已提交
5953 5954 5955 5956
static int match_filter(const struct diff_options *options, const struct diff_filepair *p)
{
	return (((p->status == DIFF_STATUS_MODIFIED) &&
		 ((p->score &&
5957
		   filter_bit_tst(DIFF_STATUS_FILTER_BROKEN, options)) ||
J
Junio C Hamano 已提交
5958
		  (!p->score &&
5959
		   filter_bit_tst(DIFF_STATUS_MODIFIED, options)))) ||
J
Junio C Hamano 已提交
5960
		((p->status != DIFF_STATUS_MODIFIED) &&
5961
		 filter_bit_tst(p->status, options)));
J
Junio C Hamano 已提交
5962 5963
}

5964
static void diffcore_apply_filter(struct diff_options *options)
J
Junio C Hamano 已提交
5965 5966 5967 5968
{
	int i;
	struct diff_queue_struct *q = &diff_queued_diff;
	struct diff_queue_struct outq;
5969

B
Bo Yang 已提交
5970
	DIFF_QUEUE_CLEAR(&outq);
J
Junio C Hamano 已提交
5971

5972
	if (!options->filter)
J
Junio C Hamano 已提交
5973 5974
		return;

5975
	if (filter_bit_tst(DIFF_STATUS_FILTER_AON, options)) {
J
Junio C Hamano 已提交
5976 5977
		int found;
		for (i = found = 0; !found && i < q->nr; i++) {
J
Junio C Hamano 已提交
5978
			if (match_filter(options, q->queue[i]))
J
Junio C Hamano 已提交
5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995
				found++;
		}
		if (found)
			return;

		/* otherwise we will clear the whole queue
		 * by copying the empty outq at the end of this
		 * function, but first clear the current entries
		 * in the queue.
		 */
		for (i = 0; i < q->nr; i++)
			diff_free_filepair(q->queue[i]);
	}
	else {
		/* Only the matching ones */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
J
Junio C Hamano 已提交
5996
			if (match_filter(options, p))
J
Junio C Hamano 已提交
5997 5998 5999 6000 6001 6002 6003 6004 6005
				diff_q(&outq, p);
			else
				diff_free_filepair(p);
		}
	}
	free(q->queue);
	*q = outq;
}

6006 6007 6008 6009
/* Check whether two filespecs with the same mode and size are identical */
static int diff_filespec_is_identical(struct diff_filespec *one,
				      struct diff_filespec *two)
{
6010 6011
	if (S_ISGITLINK(one->mode))
		return 0;
6012 6013 6014 6015 6016 6017 6018
	if (diff_populate_filespec(one, 0))
		return 0;
	if (diff_populate_filespec(two, 0))
		return 0;
	return !memcmp(one->data, two->data, one->size);
}

6019 6020
static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
{
6021 6022 6023 6024 6025
	if (p->done_skip_stat_unmatch)
		return p->skip_stat_unmatch_result;

	p->done_skip_stat_unmatch = 1;
	p->skip_stat_unmatch_result = 0;
6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040
	/*
	 * 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) ||
6041
	    (p->one->oid_valid && p->two->oid_valid) ||
6042
	    (p->one->mode != p->two->mode) ||
6043 6044
	    diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
	    diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
6045 6046
	    (p->one->size != p->two->size) ||
	    !diff_filespec_is_identical(p->one, p->two)) /* (2) */
6047 6048
		p->skip_stat_unmatch_result = 1;
	return p->skip_stat_unmatch_result;
6049 6050
}

J
Junio C Hamano 已提交
6051 6052 6053 6054 6055
static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
{
	int i;
	struct diff_queue_struct *q = &diff_queued_diff;
	struct diff_queue_struct outq;
B
Bo Yang 已提交
6056
	DIFF_QUEUE_CLEAR(&outq);
J
Junio C Hamano 已提交
6057 6058 6059 6060

	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];

6061
		if (diff_filespec_check_stat_unmatch(p))
J
Junio C Hamano 已提交
6062 6063 6064 6065 6066 6067 6068
			diff_q(&outq, p);
		else {
			/*
			 * The caller can subtract 1 from skip_stat_unmatch
			 * to determine how many paths were dirty only
			 * due to stat info mismatch.
			 */
6069
			if (!diffopt->flags.no_index)
6070
				diffopt->skip_stat_unmatch++;
J
Junio C Hamano 已提交
6071 6072 6073 6074 6075 6076 6077
			diff_free_filepair(p);
		}
	}
	free(q->queue);
	*q = outq;
}

6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091
static int diffnamecmp(const void *a_, const void *b_)
{
	const struct diff_filepair *a = *((const struct diff_filepair **)a_);
	const struct diff_filepair *b = *((const struct diff_filepair **)b_);
	const char *name_a, *name_b;

	name_a = a->one ? a->one->path : a->two->path;
	name_b = b->one ? b->one->path : b->two->path;
	return strcmp(name_a, name_b);
}

void diffcore_fix_diff_index(struct diff_options *options)
{
	struct diff_queue_struct *q = &diff_queued_diff;
R
René Scharfe 已提交
6092
	QSORT(q->queue, q->nr, diffnamecmp);
6093 6094
}

J
Junio C Hamano 已提交
6095 6096
void diffcore_std(struct diff_options *options)
{
6097
	/* NOTE please keep the following in sync with diff_tree_combined() */
6098
	if (options->skip_stat_unmatch)
J
Junio C Hamano 已提交
6099
		diffcore_skip_stat_unmatch(options);
6100 6101 6102 6103 6104 6105 6106 6107 6108
	if (!options->found_follow) {
		/* See try_to_follow_renames() in tree-diff.c */
		if (options->break_opt != -1)
			diffcore_break(options->break_opt);
		if (options->detect_rename)
			diffcore_rename(options);
		if (options->break_opt != -1)
			diffcore_merge_broken();
	}
6109
	if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)
6110
		diffcore_pickaxe(options);
J
Junio C Hamano 已提交
6111 6112
	if (options->orderfile)
		diffcore_order(options->orderfile);
6113 6114 6115
	if (!options->found_follow)
		/* See try_to_follow_renames() in tree-diff.c */
		diff_resolve_rename_copy();
6116
	diffcore_apply_filter(options);
J
Junio C Hamano 已提交
6117

6118 6119
	if (diff_queued_diff.nr && !options->flags.diff_from_contents)
		options->flags.has_changes = 1;
6120
	else
6121
		options->flags.has_changes = 0;
6122

6123
	options->found_follow = 0;
J
Junio C Hamano 已提交
6124 6125
}

J
Junio C Hamano 已提交
6126 6127 6128
int diff_result_code(struct diff_options *opt, int status)
{
	int result = 0;
6129

6130
	diff_warn_rename_limit("diff.renameLimit",
6131 6132
			       opt->needed_rename_limit,
			       opt->degraded_cc_to_c);
6133
	if (!opt->flags.exit_with_status &&
J
Junio C Hamano 已提交
6134 6135
	    !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
		return status;
6136 6137
	if (opt->flags.exit_with_status &&
	    opt->flags.has_changes)
J
Junio C Hamano 已提交
6138 6139
		result |= 01;
	if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) &&
6140
	    opt->flags.check_failed)
J
Junio C Hamano 已提交
6141 6142 6143
		result |= 02;
	return result;
}
J
Junio C Hamano 已提交
6144

6145 6146
int diff_can_quit_early(struct diff_options *opt)
{
6147
	return (opt->flags.quick &&
6148
		!opt->filter &&
6149
		opt->flags.has_changes);
6150 6151
}

6152 6153 6154 6155 6156 6157 6158 6159 6160
/*
 * Shall changes to this submodule be ignored?
 *
 * Submodule changes can be configured to be ignored separately for each path,
 * but that configuration can be overridden from the command line.
 */
static int is_submodule_ignored(const char *path, struct diff_options *options)
{
	int ignored = 0;
6161
	struct diff_flags orig_flags = options->flags;
6162
	if (!options->flags.override_submodule_config)
6163
		set_diffopt_flags_from_submodule_config(options, path);
6164
	if (options->flags.ignore_submodules)
6165 6166 6167 6168 6169
		ignored = 1;
	options->flags = orig_flags;
	return ignored;
}

J
Junio C Hamano 已提交
6170 6171
void diff_addremove(struct diff_options *options,
		    int addremove, unsigned mode,
6172 6173
		    const struct object_id *oid,
		    int oid_valid,
6174
		    const char *concatpath, unsigned dirty_submodule)
J
Junio C Hamano 已提交
6175 6176 6177
{
	struct diff_filespec *one, *two;

6178
	if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
6179 6180
		return;

J
Junio C Hamano 已提交
6181 6182 6183 6184 6185 6186 6187
	/* This may look odd, but it is a preparation for
	 * feeding "there are unchanged files which should
	 * not produce diffs, but when you are doing copy
	 * detection you would need them, so here they are"
	 * entries to the diff-core.  They will be prefixed
	 * with something like '=' or '*' (I haven't decided
	 * which but should not make any difference).
J
Junio C Hamano 已提交
6188
	 * Feeding the same new and old to diff_change()
J
Junio C Hamano 已提交
6189 6190 6191 6192
	 * also has the same effect.
	 * Before the final output happens, they are pruned after
	 * merged into rename/copy pairs as appropriate.
	 */
6193
	if (options->flags.reverse_diff)
J
Junio C Hamano 已提交
6194 6195 6196
		addremove = (addremove == '+' ? '-' :
			     addremove == '-' ? '+' : addremove);

6197 6198 6199 6200
	if (options->prefix &&
	    strncmp(concatpath, options->prefix, options->prefix_length))
		return;

J
Junio C Hamano 已提交
6201 6202 6203 6204
	one = alloc_filespec(concatpath);
	two = alloc_filespec(concatpath);

	if (addremove != '+')
6205
		fill_filespec(one, oid, oid_valid, mode);
6206
	if (addremove != '-') {
6207
		fill_filespec(two, oid, oid_valid, mode);
6208 6209
		two->dirty_submodule = dirty_submodule;
	}
J
Junio C Hamano 已提交
6210 6211

	diff_queue(&diff_queued_diff, one, two);
6212 6213
	if (!options->flags.diff_from_contents)
		options->flags.has_changes = 1;
J
Junio C Hamano 已提交
6214 6215 6216 6217
}

void diff_change(struct diff_options *options,
		 unsigned old_mode, unsigned new_mode,
6218 6219 6220
		 const struct object_id *old_oid,
		 const struct object_id *new_oid,
		 int old_oid_valid, int new_oid_valid,
6221 6222
		 const char *concatpath,
		 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
J
Junio C Hamano 已提交
6223 6224
{
	struct diff_filespec *one, *two;
6225
	struct diff_filepair *p;
J
Junio C Hamano 已提交
6226

6227 6228
	if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
	    is_submodule_ignored(concatpath, options))
6229 6230
		return;

6231
	if (options->flags.reverse_diff) {
R
René Scharfe 已提交
6232
		SWAP(old_mode, new_mode);
6233 6234
		SWAP(old_oid, new_oid);
		SWAP(old_oid_valid, new_oid_valid);
R
René Scharfe 已提交
6235
		SWAP(old_dirty_submodule, new_dirty_submodule);
J
Junio C Hamano 已提交
6236
	}
6237 6238 6239 6240 6241

	if (options->prefix &&
	    strncmp(concatpath, options->prefix, options->prefix_length))
		return;

J
Junio C Hamano 已提交
6242 6243
	one = alloc_filespec(concatpath);
	two = alloc_filespec(concatpath);
6244 6245
	fill_filespec(one, old_oid, old_oid_valid, old_mode);
	fill_filespec(two, new_oid, new_oid_valid, new_mode);
6246 6247
	one->dirty_submodule = old_dirty_submodule;
	two->dirty_submodule = new_dirty_submodule;
6248
	p = diff_queue(&diff_queued_diff, one, two);
J
Junio C Hamano 已提交
6249

6250
	if (options->flags.diff_from_contents)
6251 6252
		return;

6253
	if (options->flags.quick && options->skip_stat_unmatch &&
6254 6255 6256
	    !diff_filespec_check_stat_unmatch(p))
		return;

6257
	options->flags.has_changes = 1;
J
Junio C Hamano 已提交
6258 6259
}

6260
struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
J
Junio C Hamano 已提交
6261
{
6262
	struct diff_filepair *pair;
J
Junio C Hamano 已提交
6263
	struct diff_filespec *one, *two;
6264 6265 6266

	if (options->prefix &&
	    strncmp(path, options->prefix, options->prefix_length))
6267
		return NULL;
6268

J
Junio C Hamano 已提交
6269 6270
	one = alloc_filespec(path);
	two = alloc_filespec(path);
6271 6272 6273
	pair = diff_queue(&diff_queued_diff, one, two);
	pair->is_unmerged = 1;
	return pair;
J
Junio C Hamano 已提交
6274
}
6275 6276 6277 6278

static char *run_textconv(const char *pgm, struct diff_filespec *spec,
		size_t *outsize)
{
6279
	struct diff_tempfile *temp;
6280 6281
	const char *argv[3];
	const char **arg = argv;
6282
	struct child_process child = CHILD_PROCESS_INIT;
6283
	struct strbuf buf = STRBUF_INIT;
J
Johannes Sixt 已提交
6284
	int err = 0;
6285

6286
	temp = prepare_temp_file(spec->path, spec);
6287
	*arg++ = pgm;
6288
	*arg++ = temp->name;
6289 6290
	*arg = NULL;

J
Jeff King 已提交
6291
	child.use_shell = 1;
6292 6293
	child.argv = argv;
	child.out = -1;
J
Johannes Sixt 已提交
6294
	if (start_command(&child)) {
6295
		remove_tempfile();
6296 6297
		return NULL;
	}
J
Johannes Sixt 已提交
6298 6299 6300

	if (strbuf_read(&buf, child.out, 0) < 0)
		err = error("error reading from textconv command '%s'", pgm);
6301
	close(child.out);
J
Johannes Sixt 已提交
6302 6303 6304 6305 6306 6307

	if (finish_command(&child) || err) {
		strbuf_release(&buf);
		remove_tempfile();
		return NULL;
	}
6308
	remove_tempfile();
6309 6310 6311

	return strbuf_detach(&buf, outsize);
}
6312

A
Axel Bonnet 已提交
6313 6314 6315
size_t fill_textconv(struct userdiff_driver *driver,
		     struct diff_filespec *df,
		     char **outbuf)
6316 6317 6318
{
	size_t size;

J
Jeff King 已提交
6319
	if (!driver) {
6320 6321 6322 6323 6324 6325 6326 6327 6328 6329
		if (!DIFF_FILE_VALID(df)) {
			*outbuf = "";
			return 0;
		}
		if (diff_populate_filespec(df, 0))
			die("unable to read files to diff");
		*outbuf = df->data;
		return df->size;
	}

J
Jeff King 已提交
6330
	if (!driver->textconv)
6331
		BUG("fill_textconv called with non-textconv driver");
J
Jeff King 已提交
6332

6333
	if (driver->textconv_cache && df->oid_valid) {
6334
		*outbuf = notes_cache_get(driver->textconv_cache,
6335
					  &df->oid,
J
Jeff King 已提交
6336 6337 6338 6339 6340 6341
					  &size);
		if (*outbuf)
			return size;
	}

	*outbuf = run_textconv(driver->textconv, df, &size);
6342 6343
	if (!*outbuf)
		die("unable to read files to diff");
J
Jeff King 已提交
6344

6345
	if (driver->textconv_cache && df->oid_valid) {
J
Jeff King 已提交
6346
		/* ignore errors, as we might be in a readonly repository */
6347
		notes_cache_put(driver->textconv_cache, &df->oid, *outbuf,
J
Jeff King 已提交
6348 6349 6350 6351 6352 6353 6354 6355 6356 6357
				size);
		/*
		 * we could save up changes and flush them all at the end,
		 * but we would need an extra call after all diffing is done.
		 * Since generating a cache entry is the slow path anyway,
		 * this extra overhead probably isn't a big deal.
		 */
		notes_cache_write(driver->textconv_cache);
	}

6358 6359
	return size;
}
6360

6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371
int textconv_object(const char *path,
		    unsigned mode,
		    const struct object_id *oid,
		    int oid_valid,
		    char **buf,
		    unsigned long *buf_size)
{
	struct diff_filespec *df;
	struct userdiff_driver *textconv;

	df = alloc_filespec(path);
J
Junio C Hamano 已提交
6372
	fill_filespec(df, oid, oid_valid, mode);
6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383
	textconv = get_textconv(df);
	if (!textconv) {
		free_filespec(df);
		return 0;
	}

	*buf_size = fill_textconv(textconv, df, buf);
	free_filespec(df);
	return 1;
}

6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394
void setup_diff_pager(struct diff_options *opt)
{
	/*
	 * If the user asked for our exit code, then either they want --quiet
	 * or --exit-code. We should definitely not bother with a pager in the
	 * former case, as we will generate no output. Since we still properly
	 * report our exit code even when a pager is run, we _could_ run a
	 * pager with --exit-code. But since we have not done so historically,
	 * and because it is easy to find people oneline advising "git diff
	 * --exit-code" in hooks and other scripts, we do not do so.
	 */
6395
	if (!opt->flags.exit_with_status &&
6396 6397 6398
	    check_pager_config("diff") != 0)
		setup_pager();
}