diff.c 18.6 KB
Newer Older
1 2 3 4 5
/*
 * Copyright (C) 2005 Junio C Hamano
 */
#include <sys/types.h>
#include <sys/wait.h>
6
#include <signal.h>
7
#include <limits.h>
8 9
#include "cache.h"
#include "diff.h"
10
#include "diffcore.h"
11

J
Junio C Hamano 已提交
12
static const char *diff_opts = "-pu";
13
static unsigned char null_sha1[20] = { 0, };
14 15

static int reverse_diff;
16 17 18
static int generate_patch;
static int line_termination = '\n';
static int inter_name_termination = '\t';
19 20
static const char **pathspec;
static int speccnt;
21

22
static const char *external_diff(void)
23
{
J
Junio C Hamano 已提交
24
	static const char *external_diff_cmd = NULL;
25 26 27 28 29
	static int done_preparing = 0;

	if (done_preparing)
		return external_diff_cmd;

30 31 32 33 34 35 36 37
	/*
	 * Default values above are meant to match the
	 * Linux kernel development style.  Examples of
	 * alternative styles you can specify via environment
	 * variables are:
	 *
	 * GIT_DIFF_OPTS="-c";
	 */
J
Junio C Hamano 已提交
38 39
	if (gitenv("GIT_EXTERNAL_DIFF"))
		external_diff_cmd = gitenv("GIT_EXTERNAL_DIFF");
40 41

	/* In case external diff fails... */
J
Junio C Hamano 已提交
42
	diff_opts = gitenv("GIT_DIFF_OPTS") ? : diff_opts;
43 44 45

	done_preparing = 1;
	return external_diff_cmd;
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
}

/* Help to copy the thing properly quoted for the shell safety.
 * any single quote is replaced with '\'', and the caller is
 * expected to enclose the result within a single quote pair.
 *
 * E.g.
 *  original     sq_expand     result
 *  name     ==> name      ==> 'name'
 *  a b      ==> a b       ==> 'a b'
 *  a'b      ==> a'\''b    ==> 'a'\''b'
 */
static char *sq_expand(const char *src)
{
	static char *buf = NULL;
	int cnt, c;
	const char *cp;
	char *bp;

J
Junio C Hamano 已提交
65
	/* count bytes needed to store the quoted string. */
66 67 68 69
	for (cnt = 1, cp = src; *cp; cnt++, cp++)
		if (*cp == '\'')
			cnt += 3;

70
	buf = xmalloc(cnt);
71 72 73 74 75 76 77 78 79 80 81 82 83
	bp = buf;
	while ((c = *src++)) {
		if (c != '\'')
			*bp++ = c;
		else {
			bp = strcpy(bp, "'\\''");
			bp += 4;
		}
	}
	*bp = 0;
	return buf;
}

84
static struct diff_tempfile {
85
	const char *name; /* filename external diff should read from */
86 87 88 89 90
	char hex[41];
	char mode[10];
	char tmp_path[50];
} diff_temp[2];

J
Junio C Hamano 已提交
91 92
static void builtin_diff(const char *name_a,
			 const char *name_b,
J
Junio C Hamano 已提交
93
			 struct diff_tempfile *temp,
94
			 const char *xfrm_msg)
95
{
96
	int i, next_at, cmd_size;
97
	const char *diff_cmd = "diff -L'%s%s' -L'%s%s'";
98
	const char *diff_arg  = "'%s' '%s'||:"; /* "||:" is to return 0 */
99 100 101
	const char *input_name_sq[2];
	const char *path0[2];
	const char *path1[2];
J
Junio C Hamano 已提交
102
	const char *name_sq[2];
103
	char *cmd;
J
Junio C Hamano 已提交
104 105 106 107

	name_sq[0] = sq_expand(name_a);
	name_sq[1] = sq_expand(name_b);

108 109
	/* diff_cmd and diff_arg have 6 %s in total which makes
	 * the sum of these strings 12 bytes larger than required.
110
	 * we use 2 spaces around diff-opts, and we need to count
111
	 * terminating NUL, so we subtract 9 here.
112
	 */
113
	cmd_size = (strlen(diff_cmd) + strlen(diff_opts) +
114
			strlen(diff_arg) - 9);
115 116 117 118 119 120
	for (i = 0; i < 2; i++) {
		input_name_sq[i] = sq_expand(temp[i].name);
		if (!strcmp(temp[i].name, "/dev/null")) {
			path0[i] = "/dev/null";
			path1[i] = "";
		} else {
121
			path0[i] = i ? "b/" : "a/";
J
Junio C Hamano 已提交
122
			path1[i] = name_sq[i];
123 124
		}
		cmd_size += (strlen(path0[i]) + strlen(path1[i]) +
125
			     strlen(input_name_sq[i]));
126
	}
127

128 129 130
	cmd = xmalloc(cmd_size);

	next_at = 0;
131
	next_at += snprintf(cmd+next_at, cmd_size-next_at,
132
			    diff_cmd,
133
			    path0[0], path1[0], path0[1], path1[1]);
134 135 136
	next_at += snprintf(cmd+next_at, cmd_size-next_at,
			    " %s ", diff_opts);
	next_at += snprintf(cmd+next_at, cmd_size-next_at,
137 138
			    diff_arg, input_name_sq[0], input_name_sq[1]);

J
Junio C Hamano 已提交
139
	printf("diff --git a/%s b/%s\n", name_a, name_b);
140
	if (!path1[0][0])
141
		printf("new file mode %s\n", temp[1].mode);
142
	else if (!path1[1][0])
143
		printf("deleted file mode %s\n", temp[0].mode);
144
	else {
145 146 147 148
		if (strcmp(temp[0].mode, temp[1].mode)) {
			printf("old mode %s\n", temp[0].mode);
			printf("new mode %s\n", temp[1].mode);
		}
149 150 151
		if (xfrm_msg && xfrm_msg[0])
			fputs(xfrm_msg, stdout);

152 153 154 155
		if (strncmp(temp[0].mode, temp[1].mode, 3))
			/* we do not run diff between different kind
			 * of objects.
			 */
156 157
			exit(0);
	}
158
	fflush(NULL);
159
	execlp("/bin/sh","sh", "-c", cmd, NULL);
160 161
}

162 163 164 165 166 167
struct diff_filespec *alloc_filespec(const char *path)
{
	int namelen = strlen(path);
	struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
	spec->path = (char *)(spec + 1);
	strcpy(spec->path, path);
168
	spec->should_free = spec->should_munmap = 0;
169 170 171
	spec->xfrm_flags = 0;
	spec->size = 0;
	spec->data = 0;
172 173
	spec->mode = 0;
	memset(spec->sha1, 0, 20);
174 175 176 177 178 179
	return spec;
}

void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
		   unsigned short mode)
{
180 181 182 183 184
	if (mode) { /* just playing defensive */
		spec->mode = mode;
		memcpy(spec->sha1, sha1, 20);
		spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
	}
185 186
}

J
Junio C Hamano 已提交
187 188 189 190 191 192 193 194 195 196
/*
 * Given a name and sha1 pair, if the dircache tells us the file in
 * the work tree has that object contents, return true, so that
 * prepare_temp_file() does not have to inflate and extract.
 */
static int work_tree_matches(const char *name, const unsigned char *sha1)
{
	struct cache_entry *ce;
	struct stat st;
	int pos, len;
197

J
Junio C Hamano 已提交
198 199 200 201
	/* We do not read the cache ourselves here, because the
	 * 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
J
Junio C Hamano 已提交
202
	 * tree.  This is because most diff-tree comparisons deal with
J
Junio C Hamano 已提交
203 204 205 206 207 208
	 * 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.
J
Junio C Hamano 已提交
209
	 */
J
Junio C Hamano 已提交
210 211 212 213 214 215 216 217
	if (!active_cache)
		return 0;

	len = strlen(name);
	pos = cache_name_pos(name, len);
	if (pos < 0)
		return 0;
	ce = active_cache[pos];
218
	if ((lstat(name, &st) < 0) ||
219
	    !S_ISREG(st.st_mode) || /* careful! */
220
	    ce_match_stat(ce, &st) ||
J
Junio C Hamano 已提交
221 222
	    memcmp(sha1, ce->sha1, 20))
		return 0;
223 224 225 226 227
	/* we return 1 only when we can stat, it is a regular file,
	 * stat information matches, and sha1 recorded in the cache
	 * matches.  I.e. we know the file in the work tree really is
	 * the same as the <name, sha1> pair.
	 */
J
Junio C Hamano 已提交
228 229 230
	return 1;
}

231 232 233 234 235 236 237 238
/*
 * 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.
 */
int diff_populate_filespec(struct diff_filespec *s)
{
	int err = 0;
239
	if (!DIFF_FILE_VALID(s))
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
		die("internal error: asking to populate invalid file.");
	if (S_ISDIR(s->mode))
		return -1;

	if (s->data)
		return err;
	if (!s->sha1_valid ||
	    work_tree_matches(s->path, s->sha1)) {
		struct stat st;
		int fd;
		if (lstat(s->path, &st) < 0) {
			if (errno == ENOENT) {
			err_empty:
				err = -1;
			empty:
				s->data = "";
				s->size = 0;
				return err;
			}
		}
		s->size = st.st_size;
		if (!s->size)
			goto empty;
		if (S_ISLNK(st.st_mode)) {
			int ret;
			s->data = xmalloc(s->size);
			s->should_free = 1;
			ret = readlink(s->path, s->data, s->size);
			if (ret < 0) {
				free(s->data);
				goto err_empty;
			}
			return 0;
		}
		fd = open(s->path, O_RDONLY);
		if (fd < 0)
			goto err_empty;
		s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
		s->should_munmap = 1;
		close(fd);
	}
	else {
		char type[20];
		s->data = read_sha1_file(s->sha1, type, &s->size);
		s->should_free = 1;
	}
	return 0;
}

void diff_free_filespec_data(struct diff_filespec *s)
{
	if (s->should_free)
		free(s->data);
	else if (s->should_munmap)
		munmap(s->data, s->size);
	s->should_free = s->should_munmap = 0;
	s->data = 0;
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
static void prep_temp_blob(struct diff_tempfile *temp,
			   void *blob,
			   unsigned long size,
			   unsigned char *sha1,
			   int mode)
{
	int fd;

	strcpy(temp->tmp_path, ".diff_XXXXXX");
	fd = mkstemp(temp->tmp_path);
	if (fd < 0)
		die("unable to create temp-file");
	if (write(fd, blob, size) != size)
		die("unable to write temp-file");
	close(fd);
	temp->name = temp->tmp_path;
	strcpy(temp->hex, sha1_to_hex(sha1));
	temp->hex[40] = 0;
	sprintf(temp->mode, "%06o", mode);
}

320 321
static void prepare_temp_file(const char *name,
			      struct diff_tempfile *temp,
322
			      struct diff_filespec *one)
323
{
324
	if (!DIFF_FILE_VALID(one)) {
325
	not_a_valid_file:
326 327 328
		/* A '-' entry produces this for file-2, and
		 * a '+' entry produces this for file-1.
		 */
329 330 331
		temp->name = "/dev/null";
		strcpy(temp->hex, ".");
		strcpy(temp->mode, ".");
332 333
		return;
	}
334

335
	if (!one->sha1_valid ||
336
	    work_tree_matches(name, one->sha1)) {
337
		struct stat st;
338
		if (lstat(name, &st) < 0) {
339 340
			if (errno == ENOENT)
				goto not_a_valid_file;
341
			die("stat(%s): %s", name, strerror(errno));
342
		}
343 344 345 346 347 348 349 350 351 352
		if (S_ISLNK(st.st_mode)) {
			int ret;
			char *buf, buf_[1024];
			buf = ((sizeof(buf_) < st.st_size) ?
			       xmalloc(st.st_size) : buf_);
			ret = readlink(name, buf, st.st_size);
			if (ret < 0)
				die("readlink(%s)", name);
			prep_temp_blob(temp, buf, st.st_size,
				       (one->sha1_valid ?
353
					one->sha1 : null_sha1),
354 355 356 357
				       (one->sha1_valid ?
					one->mode : S_IFLNK));
		}
		else {
358 359
			/* we can borrow from the file in the work tree */
			temp->name = name;
360 361 362
			if (!one->sha1_valid)
				strcpy(temp->hex, sha1_to_hex(null_sha1));
			else
363
				strcpy(temp->hex, sha1_to_hex(one->sha1));
364 365 366 367
			sprintf(temp->mode, "%06o",
				S_IFREG |ce_permissions(st.st_mode));
		}
		return;
368 369
	}
	else {
370 371 372 373
		if (diff_populate_filespec(one))
			die("cannot read data blob for %s", one->path);
		prep_temp_blob(temp, one->data, one->size,
			       one->sha1, one->mode);
374 375 376 377 378 379 380 381 382 383 384 385 386 387
	}
}

static void remove_tempfile(void)
{
	int i;

	for (i = 0; i < 2; i++)
		if (diff_temp[i].name == diff_temp[i].tmp_path) {
			unlink(diff_temp[i].name);
			diff_temp[i].name = NULL;
		}
}

388 389 390 391 392
static void remove_tempfile_on_signal(int signo)
{
	remove_tempfile();
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
static int matches_pathspec(const char *name)
{
	int i;
	int namelen;

	if (speccnt == 0)
		return 1;

	namelen = strlen(name);
	for (i = 0; i < speccnt; i++) {
		int speclen = strlen(pathspec[i]);
		if (! strncmp(pathspec[i], name, speclen) &&
		    speclen <= namelen &&
		    (name[speclen] == 0 || name[speclen] == '/'))
			return 1;
	}
	return 0;
}

412 413 414
/* An external diff command takes:
 *
 * diff-cmd name infile1 infile1-sha1 infile1-mode \
415
 *               infile2 infile2-sha1 infile2-mode [ rename-to ]
416 417
 *
 */
418 419
static void run_external_diff(const char *name,
			      const char *other,
420 421 422
			      struct diff_filespec *one,
			      struct diff_filespec *two,
			      const char *xfrm_msg)
423 424
{
	struct diff_tempfile *temp = diff_temp;
425 426
	pid_t pid;
	int status;
427 428
	static int atexit_asked = 0;

429 430 431
	if (!matches_pathspec(name) && (!other || !matches_pathspec(other)))
		return;

432 433
	if (one && two) {
		prepare_temp_file(name, &temp[0], one);
J
Junio C Hamano 已提交
434
		prepare_temp_file(other ? : name, &temp[1], two);
435 436 437 438 439 440
		if (! atexit_asked &&
		    (temp[0].name == temp[0].tmp_path ||
		     temp[1].name == temp[1].tmp_path)) {
			atexit_asked = 1;
			atexit(remove_tempfile);
		}
441
		signal(SIGINT, remove_tempfile_on_signal);
442 443 444 445 446 447 448 449
	}

	fflush(NULL);
	pid = fork();
	if (pid < 0)
		die("unable to fork");
	if (!pid) {
		const char *pgm = external_diff();
450 451
		if (pgm) {
			if (one && two) {
452
				const char *exec_arg[10];
453 454 455 456 457 458 459 460 461
				const char **arg = &exec_arg[0];
				*arg++ = pgm;
				*arg++ = name;
				*arg++ = temp[0].name;
				*arg++ = temp[0].hex;
				*arg++ = temp[0].mode;
				*arg++ = temp[1].name;
				*arg++ = temp[1].hex;
				*arg++ = temp[1].mode;
462
				if (other) {
463
					*arg++ = other;
464 465 466
					*arg++ = xfrm_msg;
				}
				*arg = 0;
467 468
				execvp(pgm, (char *const*) exec_arg);
			}
469 470 471
			else
				execlp(pgm, pgm, name, NULL);
		}
472 473 474
		/*
		 * otherwise we use the built-in one.
		 */
475
		if (one && two)
476
			builtin_diff(name, other ? : name, temp, xfrm_msg);
477 478
		else
			printf("* Unmerged path %s\n", name);
479 480
		exit(0);
	}
481 482 483
	if (waitpid(pid, &status, 0) < 0 ||
	    !WIFEXITED(status) || WEXITSTATUS(status)) {
		/* Earlier we did not check the exit status because
484
		 * diff exits non-zero if files are different, and
485 486 487 488 489 490
		 * we are not interested in knowing that.  It was a
		 * mistake which made it harder to quit a diff-*
		 * session that uses the git-apply-patch-script as
		 * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
		 * should also exit non-zero only when it wants to
		 * abort the entire diff-* session.
491 492
		 */
		remove_tempfile();
493 494
		fprintf(stderr, "external diff died, stopping at %s.\n", name);
		exit(1);
495
	}
496 497 498
	remove_tempfile();
}

499
int diff_scoreopt_parse(const char *opt)
500
{
501 502 503 504 505 506 507 508 509
	int diglen, num, scale, i;
	if (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C'))
		return -1; /* that is not a -M nor -C option */
	diglen = strspn(opt+2, "0123456789");
	if (diglen == 0 || strlen(opt+2) != diglen)
		return 0; /* use default */
	sscanf(opt+2, "%d", &num);
	for (i = 0, scale = 1; i < diglen; i++)
		scale *= 10;
510

511 512 513 514
	/* user says num divided by scale and we say internally that
	 * is MAX_SCORE * num / scale.
	 */
	return MAX_SCORE * num / scale;
515 516
}

517
void diff_setup(int reverse_diff_, int diff_output_style)
518
{
519
	reverse_diff = reverse_diff_;
520 521 522 523 524 525 526 527 528 529 530 531 532
	generate_patch = 0;
	switch (diff_output_style) {
	case DIFF_FORMAT_HUMAN:
		line_termination = '\n';
		inter_name_termination = '\t';
		break;
	case DIFF_FORMAT_MACHINE:
		line_termination = inter_name_termination = 0;
		break;
	case DIFF_FORMAT_PATCH:
		generate_patch = 1;
		break;
	}
533 534
}

535
struct diff_queue_struct diff_queued_diff;
536

537
struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
538 539
				  struct diff_filespec *one,
				  struct diff_filespec *two)
540
{
541
	struct diff_filepair *dp = xmalloc(sizeof(*dp));
542 543 544 545 546 547 548 549 550
	dp->one = one;
	dp->two = two;
	dp->xfrm_msg = 0;
	dp->orig_order = queue->nr;
	dp->xfrm_work = 0;
	if (queue->alloc <= queue->nr) {
		queue->alloc = alloc_nr(queue->alloc);
		queue->queue = xrealloc(queue->queue,
				       sizeof(dp) * queue->alloc);
551
	}
552 553
	queue->queue[queue->nr++] = dp;
	return dp;
554 555
}

556
static void diff_flush_raw(struct diff_filepair *p)
557
{
558 559 560 561 562 563 564 565 566
	/*
	 * We used to reject rename/copy but new diff-raw can express them.
	 */
	printf(":%06o %06o %s ",
	       p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));
	printf("%s%c%s%c%s%c",
	       sha1_to_hex(p->two->sha1), inter_name_termination,
	       p->one->path, inter_name_termination,
	       p->two->path, line_termination);
567 568
}

569
static void diff_flush_patch(struct diff_filepair *p)
570
{
571
	const char *name, *other;
572

573 574
	name = p->one->path;
	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
575 576
	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
577
		return; /* no tree diffs in patch format */ 
578

579
	run_external_diff(name, other, p->one, p->two, p->xfrm_msg);
580 581
}

582
static int identical(struct diff_filespec *one, struct diff_filespec *two)
583
{
584 585
	/* This function is written stricter than necessary to support
	 * the currently implemented transformers, but the idea is to
586
	 * let transformers to produce diff_filepairs any way they want,
587
	 * and filter and clean them up here before producing the output.
588 589
	 */

590
	if (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two))
591
		return 1; /* not interesting */
592

593
	/* deletion, addition, mode change and renames are all interesting. */
594 595
	if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
	    (one->mode != two->mode) ||
596 597
	    strcmp(one->path, two->path))
		return 0;
J
Junio C Hamano 已提交
598

599 600
	/* both are valid and point at the same path.  that is, we are
	 * dealing with a change.
J
Junio C Hamano 已提交
601
	 */
602 603 604 605 606 607
	if (one->sha1_valid && two->sha1_valid &&
	    !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
		return 1; /* no change */
	if (!one->sha1_valid && !two->sha1_valid)
		return 1; /* both look at the same file on the filesystem. */
	return 0;
J
Junio C Hamano 已提交
608 609
}

610
static void diff_flush_one(struct diff_filepair *p)
611
{
612 613
	if (identical(p->one, p->two))
		return;
614
	if (generate_patch)
615
		diff_flush_patch(p);
616 617
	else
		diff_flush_raw(p);
J
Junio C Hamano 已提交
618 619
}

620
int diff_queue_is_empty(void)
J
Junio C Hamano 已提交
621
{
622
	struct diff_queue_struct *q = &diff_queued_diff;
623 624
	int i;

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];
		if (!identical(p->one, p->two))
			return 0;
	}
	return 1;
}

void diff_flush(const char **pathspec_, int speccnt_)
{
	struct diff_queue_struct *q = &diff_queued_diff;
	int i;

	pathspec = pathspec_;
	speccnt = speccnt_;

641 642 643 644
	for (i = 0; i < q->nr; i++)
		diff_flush_one(q->queue[i]);

	for (i = 0; i < q->nr; i++) {
645
		struct diff_filepair *p = q->queue[i];
646 647 648 649 650 651 652 653
		diff_free_filespec_data(p->one);
		diff_free_filespec_data(p->two);
		free(p->xfrm_msg);
		free(p);
	}
	free(q->queue);
	q->queue = NULL;
	q->nr = q->alloc = 0;
654 655
}

656 657 658
void diff_addremove(int addremove, unsigned mode,
		    const unsigned char *sha1,
		    const char *base, const char *path)
659
{
660
	char concatpath[PATH_MAX];
661 662 663 664 665 666 667 668 669 670 671 672 673 674
	struct diff_filespec *one, *two;

	/* 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).
	 * Feeding the same new and old to diff_change() should
	 * also have the same effect.  diff_flush() should
	 * filter the identical ones out at the final output
	 * stage.
	 */
675
	if (reverse_diff)
676 677
		addremove = (addremove == '+' ? '-' :
			     addremove == '-' ? '+' : addremove);
678

679 680 681 682
	if (!path) path = "";
	sprintf(concatpath, "%s%s", base, path);
	one = alloc_filespec(concatpath);
	two = alloc_filespec(concatpath);
683

684 685 686 687
	if (addremove != '+')
		fill_filespec(one, sha1, mode);
	if (addremove != '-')
		fill_filespec(two, sha1, mode);
688

689
	diff_queue(&diff_queued_diff, one, two);
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
void diff_guif(unsigned old_mode,
	       unsigned new_mode,
	       const unsigned char *old_sha1,
	       const unsigned char *new_sha1,
	       const char *old_path,
	       const char *new_path)
{
	struct diff_filespec *one, *two;

	if (reverse_diff) {
		unsigned tmp;
		const unsigned char *tmp_c;
		tmp = old_mode; old_mode = new_mode; new_mode = tmp;
		tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
	}
	one = alloc_filespec(old_path);
	two = alloc_filespec(new_path);
	if (old_mode)
		fill_filespec(one, old_sha1, old_mode);
	if (new_mode)
		fill_filespec(two, new_sha1, new_mode);
	diff_queue(&diff_queued_diff, one, two);
}

716 717 718
void diff_change(unsigned old_mode, unsigned new_mode,
		 const unsigned char *old_sha1,
		 const unsigned char *new_sha1,
719 720
		 const char *base, const char *path) 
{
721
	char concatpath[PATH_MAX];
722
	struct diff_filespec *one, *two;
723

724 725 726 727 728 729
	if (reverse_diff) {
		unsigned tmp;
		const unsigned char *tmp_c;
		tmp = old_mode; old_mode = new_mode; new_mode = tmp;
		tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
	}
730 731 732 733 734 735 736
	if (!path) path = "";
	sprintf(concatpath, "%s%s", base, path);
	one = alloc_filespec(concatpath);
	two = alloc_filespec(concatpath);
	fill_filespec(one, old_sha1, old_mode);
	fill_filespec(two, new_sha1, new_mode);

737
	diff_queue(&diff_queued_diff, one, two);
738
}
739

740 741
void diff_unmerge(const char *path)
{
742 743 744 745
	if (generate_patch)
		run_external_diff(path, NULL, NULL, NULL, NULL);
	else
		printf("U %s%c", path, line_termination);
746
}