diff-lib.c 18.6 KB
Newer Older
1 2 3
/*
 * Copyright (C) 2005 Junio C Hamano
 */
4
#include "cache.h"
5
#include "quote.h"
J
Junio C Hamano 已提交
6
#include "commit.h"
7
#include "diff.h"
8
#include "diffcore.h"
J
Junio C Hamano 已提交
9
#include "revision.h"
10
#include "cache-tree.h"
11
#include "path-list.h"
12
#include "unpack-trees.h"
13

J
Junio C Hamano 已提交
14
/*
J
Junio C Hamano 已提交
15
 * diff-files
J
Junio C Hamano 已提交
16
 */
17

18 19 20 21 22 23 24 25 26 27
static int read_directory(const char *path, struct path_list *list)
{
	DIR *dir;
	struct dirent *e;

	if (!(dir = opendir(path)))
		return error("Could not open directory %s", path);

	while ((e = readdir(dir)))
		if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
R
René Scharfe 已提交
28
			path_list_insert(e->d_name, list);
29 30 31 32 33

	closedir(dir);
	return 0;
}

34 35 36 37 38 39 40
static int get_mode(const char *path, int *mode)
{
	struct stat st;

	if (!path || !strcmp(path, "/dev/null"))
		*mode = 0;
	else if (!strcmp(path, "-"))
41
		*mode = create_ce_mode(0666);
42 43 44 45 46 47 48
	else if (stat(path, &st))
		return error("Could not access '%s'", path);
	else
		*mode = st.st_mode;
	return 0;
}

49 50 51 52 53
static int queue_diff(struct diff_options *o,
		const char *name1, const char *name2)
{
	int mode1 = 0, mode2 = 0;

54 55
	if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
		return -1;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

	if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
		return error("file/directory conflict: %s, %s", name1, name2);

	if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
		char buffer1[PATH_MAX], buffer2[PATH_MAX];
		struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
		int len1 = 0, len2 = 0, i1, i2, ret = 0;

		if (name1 && read_directory(name1, &p1))
			return -1;
		if (name2 && read_directory(name2, &p2)) {
			path_list_clear(&p1, 0);
			return -1;
		}

		if (name1) {
			len1 = strlen(name1);
			if (len1 > 0 && name1[len1 - 1] == '/')
				len1--;
			memcpy(buffer1, name1, len1);
			buffer1[len1++] = '/';
		}

		if (name2) {
			len2 = strlen(name2);
			if (len2 > 0 && name2[len2 - 1] == '/')
				len2--;
			memcpy(buffer2, name2, len2);
			buffer2[len2++] = '/';
		}

		for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
			const char *n1, *n2;
			int comp;

			if (i1 == p1.nr)
				comp = 1;
			else if (i2 == p2.nr)
				comp = -1;
			else
				comp = strcmp(p1.items[i1].path,
					p2.items[i2].path);

			if (comp > 0)
				n1 = NULL;
			else {
				n1 = buffer1;
				strncpy(buffer1 + len1, p1.items[i1++].path,
						PATH_MAX - len1);
			}

			if (comp < 0)
				n2 = NULL;
			else {
				n2 = buffer2;
				strncpy(buffer2 + len2, p2.items[i2++].path,
						PATH_MAX - len2);
			}

			ret = queue_diff(o, n1, n2);
		}
		path_list_clear(&p1, 0);
		path_list_clear(&p2, 0);

		return ret;
	} else {
		struct diff_filespec *d1, *d2;

125
		if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
			unsigned tmp;
			const char *tmp_c;
			tmp = mode1; mode1 = mode2; mode2 = tmp;
			tmp_c = name1; name1 = name2; name2 = tmp_c;
		}

		if (!name1)
			name1 = "/dev/null";
		if (!name2)
			name2 = "/dev/null";
		d1 = alloc_filespec(name1);
		d2 = alloc_filespec(name2);
		fill_filespec(d1, null_sha1, mode1);
		fill_filespec(d2, null_sha1, mode2);

		diff_queue(&diff_queued_diff, d1, d2);
		return 0;
	}
}

146 147 148 149
/*
 * Does the path name a blob in the working tree, or a directory
 * in the working tree?
 */
150 151
static int is_in_index(const char *path)
{
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
	int len, pos;
	struct cache_entry *ce;

	len = strlen(path);
	while (path[len-1] == '/')
		len--;
	if (!len)
		return 1; /* "." */
	pos = cache_name_pos(path, len);
	if (0 <= pos)
		return 1;
	pos = -1 - pos;
	while (pos < active_nr) {
		ce = active_cache[pos++];
		if (ce_namelen(ce) <= len ||
		    strncmp(ce->name, path, len) ||
		    (ce->name[len] > '/'))
			break; /* path cannot be a prefix */
		if (ce->name[len] == '/')
			return 1;
	}
	return 0;
174 175 176
}

static int handle_diff_files_args(struct rev_info *revs,
177 178
				  int argc, const char **argv,
				  unsigned int *options)
179
{
180
	*options = 0;
181 182 183 184 185 186 187 188 189 190

	/* revs->max_count == -2 means --no-index */
	while (1 < argc && argv[1][0] == '-') {
		if (!strcmp(argv[1], "--base"))
			revs->max_count = 1;
		else if (!strcmp(argv[1], "--ours"))
			revs->max_count = 2;
		else if (!strcmp(argv[1], "--theirs"))
			revs->max_count = 3;
		else if (!strcmp(argv[1], "-n") ||
191
				!strcmp(argv[1], "--no-index")) {
192
			revs->max_count = -2;
193 194
			DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
			DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
195
		}
196
		else if (!strcmp(argv[1], "-q"))
197
			*options |= DIFF_SILENT_ON_REMOVED;
198 199 200 201 202 203 204 205 206 207 208 209
		else
			return error("invalid option: %s", argv[1]);
		argv++; argc--;
	}

	if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {
		/*
		 * If two files are specified, and at least one is untracked,
		 * default to no-index.
		 */
		read_cache();
		if (!is_in_index(revs->diffopt.paths[0]) ||
210
					!is_in_index(revs->diffopt.paths[1])) {
211
			revs->max_count = -2;
212
			DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
213
		}
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	}

	/*
	 * Make sure there are NO revision (i.e. pending object) parameter,
	 * rev.max_count is reasonable (0 <= n <= 3),
	 * there is no other revision filtering parameters.
	 */
	if (revs->pending.nr || revs->max_count > 3 ||
	    revs->min_age != -1 || revs->max_age != -1)
		return error("no revision allowed with diff-files");

	if (revs->max_count == -1 &&
	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
		revs->combine_merges = revs->dense_combined_merges = 1;

	return 0;
}

232 233 234
static int is_outside_repo(const char *path, int nongit, const char *prefix)
{
	int i;
235
	if (nongit || !strcmp(path, "-") || is_absolute_path(path))
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		return 1;
	if (prefixcmp(path, "../"))
		return 0;
	if (!prefix)
		return 1;
	for (i = strlen(prefix); !prefixcmp(path, "../"); ) {
		while (i > 0 && prefix[i - 1] != '/')
			i--;
		if (--i < 0)
			return 1;
		path += 3;
	}
	return 0;
}

int setup_diff_no_index(struct rev_info *revs,
		int argc, const char ** argv, int nongit, const char *prefix)
{
	int i;
	for (i = 1; i < argc; i++)
256
		if (argv[i][0] != '-' || argv[i][1] == '\0')
257 258 259 260 261 262
			break;
		else if (!strcmp(argv[i], "--")) {
			i++;
			break;
		} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
			i = argc - 3;
263
			DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
			break;
		}
	if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
				!is_outside_repo(argv[i], nongit, prefix)))
		return -1;

	diff_setup(&revs->diffopt);
	for (i = 1; i < argc - 2; )
		if (!strcmp(argv[i], "--no-index"))
			i++;
		else {
			int j = diff_opt_parse(&revs->diffopt,
					argv + i, argc - i);
			if (!j)
				die("invalid diff option/value: %s", argv[i]);
			i += j;
		}
281 282 283 284 285 286

	if (prefix) {
		int len = strlen(prefix);

		revs->diffopt.paths = xcalloc(2, sizeof(char*));
		for (i = 0; i < 2; i++) {
287 288 289 290 291 292 293 294 295
			const char *p = argv[argc - 2 + i];
			/*
			 * stdin should be spelled as '-'; if you have
			 * path that is '-', spell it as ./-.
			 */
			p = (strcmp(p, "-")
			     ? xstrdup(prefix_filename(prefix, len, p))
			     : p);
			revs->diffopt.paths[i] = p;
296 297 298 299
		}
	}
	else
		revs->diffopt.paths = argv + argc - 2;
300
	revs->diffopt.nr_paths = 2;
301
	DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
302
	revs->max_count = -2;
303 304
	if (diff_setup_done(&revs->diffopt) < 0)
		die("diff_setup_done failed");
305 306 307
	return 0;
}

308 309
int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
{
310
	unsigned int options;
311

312
	if (handle_diff_files_args(revs, argc, argv, &options))
313 314
		return -1;

315
	if (DIFF_OPT_TST(&revs->diffopt, NO_INDEX)) {
316 317
		if (revs->diffopt.nr_paths != 2)
			return error("need two files/directories with --no-index");
318 319 320
		if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
				revs->diffopt.paths[1]))
			return -1;
321 322
		diffcore_std(&revs->diffopt);
		diff_flush(&revs->diffopt);
323 324 325 326 327
		/*
		 * The return code for --no-index imitates diff(1):
		 * 0 = no changes, 1 = changes, else error
		 */
		return revs->diffopt.found_changes;
328 329
	}

330 331 332 333
	if (read_cache() < 0) {
		perror("read_cache");
		return -1;
	}
334
	return run_diff_files(revs, options);
335 336
}

337
int run_diff_files(struct rev_info *revs, unsigned int option)
338
{
J
Junio C Hamano 已提交
339 340
	int entries, i;
	int diff_unmerged_stage = revs->max_count;
341
	int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
342 343
	unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
			      ? CE_MATCH_RACY_IS_DIRTY : 0);
344

J
Junio C Hamano 已提交
345 346
	if (diff_unmerged_stage < 0)
		diff_unmerged_stage = 2;
347
	entries = active_nr;
J
Junio C Hamano 已提交
348
	for (i = 0; i < entries; i++) {
349
		struct stat st;
J
Junio C Hamano 已提交
350 351 352
		unsigned int oldmode, newmode;
		struct cache_entry *ce = active_cache[i];
		int changed;
353

354 355
		if (DIFF_OPT_TST(&revs->diffopt, QUIET) &&
			DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
J
Junio C Hamano 已提交
356 357
			break;

J
Junio C Hamano 已提交
358
		if (!ce_path_match(ce, revs->prune_data))
359
			continue;
360

J
Junio C Hamano 已提交
361
		if (ce_stage(ce)) {
362
			struct combine_diff_path *dpath;
J
Junio C Hamano 已提交
363
			int num_compare_stages = 0;
364
			size_t path_len;
J
Junio C Hamano 已提交
365

366 367
			path_len = ce_namelen(ce);

368
			dpath = xmalloc(combine_diff_path_size(5, path_len));
369 370 371 372 373 374
			dpath->path = (char *) &(dpath->parent[5]);

			dpath->next = NULL;
			dpath->len = path_len;
			memcpy(dpath->path, ce->name, path_len);
			dpath->path[path_len] = '\0';
375
			hashclr(dpath->sha1);
376
			memset(&(dpath->parent[0]), 0,
377 378 379 380 381 382 383 384 385 386 387
			       sizeof(struct combine_diff_parent)*5);

			if (lstat(ce->name, &st) < 0) {
				if (errno != ENOENT && errno != ENOTDIR) {
					perror(ce->name);
					continue;
				}
				if (silent_on_removed)
					continue;
			}
			else
388
				dpath->mode = ce_mode_from_stat(ce, st.st_mode);
J
Junio C Hamano 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401

			while (i < entries) {
				struct cache_entry *nce = active_cache[i];
				int stage;

				if (strcmp(ce->name, nce->name))
					break;

				/* Stage #2 (ours) is the first parent,
				 * stage #3 (theirs) is the second.
				 */
				stage = ce_stage(nce);
				if (2 <= stage) {
402
					int mode = nce->ce_mode;
J
Junio C Hamano 已提交
403
					num_compare_stages++;
404
					hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
405
					dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
406
					dpath->parent[stage-2].status =
J
Junio C Hamano 已提交
407 408 409 410 411 412 413 414 415 416
						DIFF_STATUS_MODIFIED;
				}

				/* diff against the proper unmerged stage */
				if (stage == diff_unmerged_stage)
					ce = nce;
				i++;
			}
			/*
			 * Compensate for loop update
417
			 */
J
Junio C Hamano 已提交
418
			i--;
J
Junio C Hamano 已提交
419

J
Junio C Hamano 已提交
420
			if (revs->combine_merges && num_compare_stages == 2) {
421
				show_combined_diff(dpath, 2,
J
Junio C Hamano 已提交
422 423
						   revs->dense_combined_merges,
						   revs);
424
				free(dpath);
J
Junio C Hamano 已提交
425
				continue;
426
			}
427 428
			free(dpath);
			dpath = NULL;
429

J
Junio C Hamano 已提交
430 431 432 433
			/*
			 * Show the diff for the 'ce' if we found the one
			 * from the desired stage.
			 */
434
			diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
J
Junio C Hamano 已提交
435 436
			if (ce_stage(ce) != diff_unmerged_stage)
				continue;
437
		}
J
Junio C Hamano 已提交
438

439 440
		if (ce_uptodate(ce))
			continue;
J
Junio C Hamano 已提交
441 442 443
		if (lstat(ce->name, &st) < 0) {
			if (errno != ENOENT && errno != ENOTDIR) {
				perror(ce->name);
444 445
				continue;
			}
J
Junio C Hamano 已提交
446 447
			if (silent_on_removed)
				continue;
448
			diff_addremove(&revs->diffopt, '-', ce->ce_mode,
J
Junio C Hamano 已提交
449 450
				       ce->sha1, ce->name, NULL);
			continue;
451
		}
452
		changed = ce_match_stat(ce, &st, ce_option);
453
		if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
J
Junio C Hamano 已提交
454
			continue;
455 456
		oldmode = ce->ce_mode;
		newmode = ce_mode_from_stat(ce, st.st_mode);
J
Junio C Hamano 已提交
457 458 459
		diff_change(&revs->diffopt, oldmode, newmode,
			    ce->sha1, (changed ? null_sha1 : ce->sha1),
			    ce->name, NULL);
460

461
	}
J
Junio C Hamano 已提交
462 463 464
	diffcore_std(&revs->diffopt);
	diff_flush(&revs->diffopt);
	return 0;
465
}
466

J
Junio C Hamano 已提交
467 468 469 470 471 472 473 474 475 476
/*
 * diff-index
 */

/* A file entry went away or appeared */
static void diff_index_show_file(struct rev_info *revs,
				 const char *prefix,
				 struct cache_entry *ce,
				 unsigned char *sha1, unsigned int mode)
{
477
	diff_addremove(&revs->diffopt, prefix[0], mode,
J
Junio C Hamano 已提交
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
		       sha1, ce->name, NULL);
}

static int get_stat_data(struct cache_entry *ce,
			 unsigned char **sha1p,
			 unsigned int *modep,
			 int cached, int match_missing)
{
	unsigned char *sha1 = ce->sha1;
	unsigned int mode = ce->ce_mode;

	if (!cached) {
		static unsigned char no_sha1[20];
		int changed;
		struct stat st;
		if (lstat(ce->name, &st) < 0) {
			if (errno == ENOENT && match_missing) {
				*sha1p = sha1;
				*modep = mode;
				return 0;
			}
			return -1;
		}
		changed = ce_match_stat(ce, &st, 0);
		if (changed) {
503
			mode = ce_mode_from_stat(ce, st.st_mode);
J
Junio C Hamano 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
			sha1 = no_sha1;
		}
	}

	*sha1p = sha1;
	*modep = mode;
	return 0;
}

static void show_new_file(struct rev_info *revs,
			  struct cache_entry *new,
			  int cached, int match_missing)
{
	unsigned char *sha1;
	unsigned int mode;

	/* New file in the index: it might actually be different in
	 * the working copy.
	 */
	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
		return;

	diff_index_show_file(revs, "+", new, sha1, mode);
}

static int show_modified(struct rev_info *revs,
			 struct cache_entry *old,
			 struct cache_entry *new,
			 int report_missing,
			 int cached, int match_missing)
{
	unsigned int mode, oldmode;
	unsigned char *sha1;

	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
		if (report_missing)
			diff_index_show_file(revs, "-", old,
					     old->sha1, old->ce_mode);
		return -1;
	}

545 546 547 548 549 550 551 552 553 554 555
	if (revs->combine_merges && !cached &&
	    (hashcmp(sha1, old->sha1) || hashcmp(old->sha1, new->sha1))) {
		struct combine_diff_path *p;
		int pathlen = ce_namelen(new);

		p = xmalloc(combine_diff_path_size(2, pathlen));
		p->path = (char *) &p->parent[2];
		p->next = NULL;
		p->len = pathlen;
		memcpy(p->path, new->name, pathlen);
		p->path[pathlen] = 0;
556
		p->mode = mode;
557 558 559
		hashclr(p->sha1);
		memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
		p->parent[0].status = DIFF_STATUS_MODIFIED;
560
		p->parent[0].mode = new->ce_mode;
561 562
		hashcpy(p->parent[0].sha1, new->sha1);
		p->parent[1].status = DIFF_STATUS_MODIFIED;
563
		p->parent[1].mode = old->ce_mode;
564 565 566 567 568 569
		hashcpy(p->parent[1].sha1, old->sha1);
		show_combined_diff(p, 2, revs->dense_combined_merges, revs);
		free(p);
		return 0;
	}

J
Junio C Hamano 已提交
570
	oldmode = old->ce_mode;
571
	if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
572
	    !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
J
Junio C Hamano 已提交
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
		return 0;

	diff_change(&revs->diffopt, oldmode, mode,
		    old->sha1, sha1, old->name, NULL);
	return 0;
}

/*
 * This turns all merge entries into "stage 3". That guarantees that
 * when we read in the new tree (into "stage 1"), we won't lose sight
 * of the fact that we had unmerged entries.
 */
static void mark_merge_entries(void)
{
	int i;
	for (i = 0; i < active_nr; i++) {
		struct cache_entry *ce = active_cache[i];
		if (!ce_stage(ce))
			continue;
592
		ce->ce_flags |= CE_STAGEMASK;
J
Junio C Hamano 已提交
593 594 595
	}
}

596 597 598 599 600 601 602 603 604 605
/*
 * This gets a mix of an existing index and a tree, one pathname entry
 * at a time. The index entry may be a single stage-0 one, but it could
 * also be multiple unmerged entries (in which case idx_pos/idx_nr will
 * give you the position and number of entries in the index).
 */
static void do_oneway_diff(struct unpack_trees_options *o,
	struct cache_entry *idx,
	struct cache_entry *tree,
	int idx_pos, int idx_nr)
J
Junio C Hamano 已提交
606
{
607 608
	struct rev_info *revs = o->unpack_data;
	int match_missing, cached;
609

J
Junio C Hamano 已提交
610
	/*
611
	 * Backward compatibility wart - "diff-index -m" does
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
	 * not mean "do not ignore merges", but "match_missing".
	 *
	 * But with the revision flag parsing, that's found in
	 * "!revs->ignore_merges".
	 */
	cached = o->index_only;
	match_missing = !revs->ignore_merges;

	if (cached && idx && ce_stage(idx)) {
		if (tree)
			diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
		return;
	}

	/*
	 * Something added to the tree?
	 */
	if (!tree) {
		show_new_file(revs, idx, cached, match_missing);
		return;
	}

	/*
	 * Something removed from the tree?
636
	 */
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
	if (!idx) {
		diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
		return;
	}

	/* Show difference between old and new */
	show_modified(revs, tree, idx, 1, cached, match_missing);
}

/*
 * Count how many index entries go with the first one
 */
static inline int count_skip(const struct cache_entry *src, int pos)
{
	int skip = 1;

	/* We can only have multiple entries if the first one is not stage-0 */
	if (ce_stage(src)) {
		struct cache_entry **p = active_cache + pos;
		int namelen = ce_namelen(src);

		for (;;) {
			const struct cache_entry *ce;
			pos++;
			if (pos >= active_nr)
				break;
			ce = *++p;
			if (ce_namelen(ce) != namelen)
				break;
			if (memcmp(ce->name, src->name, namelen))
				break;
			skip++;
		}
	}
	return skip;
}

/*
 * The unpack_trees() interface is designed for merging, so
 * the different source entries are designed primarily for
 * the source trees, with the old index being really mainly
 * used for being replaced by the result.
 *
 * For diffing, the index is more important, and we only have a
 * single tree.
 *
 * We're supposed to return how many index entries we want to skip.
 *
 * This wrapper makes it all more readable, and takes care of all
 * the fairly complex unpack_trees() semantic requirements, including
 * the skipping, the path matching, the type conflict cases etc.
 */
static int oneway_diff(struct cache_entry **src,
	struct unpack_trees_options *o,
	int index_pos)
{
	int skip = 0;
	struct cache_entry *idx = src[0];
	struct cache_entry *tree = src[1];
	struct rev_info *revs = o->unpack_data;

	if (index_pos >= 0)
		skip = count_skip(idx, index_pos);

	/*
	 * Unpack-trees generates a DF/conflict entry if
	 * there was a directory in the index and a tree
	 * in the tree. From a diff standpoint, that's a
	 * delete of the tree and a create of the file.
	 */
	if (tree == o->df_conflict_entry)
		tree = NULL;

	if (ce_path_match(idx ? idx : tree, revs->prune_data))
		do_oneway_diff(o, idx, tree, index_pos, skip);

	return skip;
}

int run_diff_index(struct rev_info *revs, int cached)
{
	struct object *ent;
	struct tree *tree;
	const char *tree_name;
	struct unpack_trees_options opts;
	struct tree_desc t;
J
Junio C Hamano 已提交
723 724 725

	mark_merge_entries();

726 727
	ent = revs->pending.objects[0].item;
	tree_name = revs->pending.objects[0].name;
J
Junio C Hamano 已提交
728 729 730
	tree = parse_tree_indirect(ent->sha1);
	if (!tree)
		return error("bad tree object %s", tree_name);
731 732 733 734 735 736 737 738 739 740 741

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = 1;
	opts.index_only = cached;
	opts.merge = 1;
	opts.fn = oneway_diff;
	opts.unpack_data = revs;

	init_tree_desc(&t, tree->buffer, tree->size);
	unpack_trees(1, &t, &opts);

J
Junio C Hamano 已提交
742 743
	diffcore_std(&revs->diffopt);
	diff_flush(&revs->diffopt);
744
	return 0;
J
Junio C Hamano 已提交
745
}
746 747 748 749 750 751 752 753

int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
{
	struct tree *tree;
	struct rev_info revs;
	int i;
	struct cache_entry **dst;
	struct cache_entry *last = NULL;
754 755
	struct unpack_trees_options opts;
	struct tree_desc t;
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771

	/*
	 * This is used by git-blame to run diff-cache internally;
	 * it potentially needs to repeatedly run this, so we will
	 * start by removing the higher order entries the last round
	 * left behind.
	 */
	dst = active_cache;
	for (i = 0; i < active_nr; i++) {
		struct cache_entry *ce = active_cache[i];
		if (ce_stage(ce)) {
			if (last && !strcmp(ce->name, last->name))
				continue;
			cache_tree_invalidate_path(active_cache_tree,
						   ce->name);
			last = ce;
772
			ce->ce_flags |= CE_REMOVE;
773 774 775 776 777 778 779 780 781 782
		}
		*dst++ = ce;
	}
	active_nr = dst - active_cache;

	init_revisions(&revs, NULL);
	revs.prune_data = opt->paths;
	tree = parse_tree_indirect(tree_sha1);
	if (!tree)
		die("bad tree object %s", sha1_to_hex(tree_sha1));
783 784 785 786 787 788 789 790 791 792 793

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = 1;
	opts.index_only = 1;
	opts.merge = 1;
	opts.fn = oneway_diff;
	opts.unpack_data = &revs;

	init_tree_desc(&t, tree->buffer, tree->size);
	unpack_trees(1, &t, &opts);
	return 0;
794
}