diffcore-rename.c 15.8 KB
Newer Older
1 2 3 4 5 6
/*
 * Copyright (C) 2005 Junio C Hamano
 */
#include "cache.h"
#include "diff.h"
#include "diffcore.h"
7
#include "hash.h"
8

9 10 11 12 13 14 15
/* Table of rename/copy destinations */

static struct diff_rename_dst {
	struct diff_filespec *two;
	struct diff_filepair *pair;
} *rename_dst;
static int rename_dst_nr, rename_dst_alloc;
16

17 18
static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
						 int insert_ok)
19
{
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
	int first, last;

	first = 0;
	last = rename_dst_nr;
	while (last > first) {
		int next = (last + first) >> 1;
		struct diff_rename_dst *dst = &(rename_dst[next]);
		int cmp = strcmp(two->path, dst->two->path);
		if (!cmp)
			return dst;
		if (cmp < 0) {
			last = next;
			continue;
		}
		first = next+1;
	}
	/* not found */
	if (!insert_ok)
		return NULL;
	/* insert to make it at "first" */
	if (rename_dst_alloc <= rename_dst_nr) {
		rename_dst_alloc = alloc_nr(rename_dst_alloc);
		rename_dst = xrealloc(rename_dst,
				      rename_dst_alloc * sizeof(*rename_dst));
	}
	rename_dst_nr++;
	if (first < rename_dst_nr)
		memmove(rename_dst + first + 1, rename_dst + first,
			(rename_dst_nr - first - 1) * sizeof(*rename_dst));
J
Junio C Hamano 已提交
49 50
	rename_dst[first].two = alloc_filespec(two->path);
	fill_filespec(rename_dst[first].two, two->sha1, two->mode);
51 52
	rename_dst[first].pair = NULL;
	return &(rename_dst[first]);
53 54
}

55
/* Table of rename/copy src files */
56 57
static struct diff_rename_src {
	struct diff_filespec *one;
58
	unsigned short score; /* to remember the break score */
59 60
} *rename_src;
static int rename_src_nr, rename_src_alloc;
61

62
static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
63
						   unsigned short score)
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
{
	int first, last;

	first = 0;
	last = rename_src_nr;
	while (last > first) {
		int next = (last + first) >> 1;
		struct diff_rename_src *src = &(rename_src[next]);
		int cmp = strcmp(one->path, src->one->path);
		if (!cmp)
			return src;
		if (cmp < 0) {
			last = next;
			continue;
		}
		first = next+1;
	}
81

82 83 84 85 86
	/* insert to make it at "first" */
	if (rename_src_alloc <= rename_src_nr) {
		rename_src_alloc = alloc_nr(rename_src_alloc);
		rename_src = xrealloc(rename_src,
				      rename_src_alloc * sizeof(*rename_src));
87
	}
88 89 90 91 92
	rename_src_nr++;
	if (first < rename_src_nr)
		memmove(rename_src + first + 1, rename_src + first,
			(rename_src_nr - first - 1) * sizeof(*rename_src));
	rename_src[first].one = one;
93
	rename_src[first].score = score;
94
	return &(rename_src[first]);
95 96
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
{
	int src_len = strlen(src->path), dst_len = strlen(dst->path);
	while (src_len && dst_len) {
		char c1 = src->path[--src_len];
		char c2 = dst->path[--dst_len];
		if (c1 != c2)
			return 0;
		if (c1 == '/')
			return 1;
	}
	return (!src_len || src->path[src_len - 1] == '/') &&
		(!dst_len || dst->path[dst_len - 1] == '/');
}

112
struct diff_score {
113 114
	int src; /* index in rename_src */
	int dst; /* index in rename_dst */
115
	int score;
116
	int name_score;
117 118 119 120 121 122 123 124 125 126 127 128 129
};

static int estimate_similarity(struct diff_filespec *src,
			       struct diff_filespec *dst,
			       int minimum_score)
{
	/* src points at a file that existed in the original tree (or
	 * optionally a file in the destination tree) and dst points
	 * at a newly created file.  They may be quite similar, in which
	 * case we want to say src is renamed to dst or src is copied into
	 * dst, and then some edit has been applied to dst.
	 *
	 * Compare them and return how similar they are, representing
130 131 132 133 134
	 * the score as an integer between 0 and MAX_SCORE.
	 *
	 * When there is an exact match, it is considered a better
	 * match than anything else; the destination does not even
	 * call into this function in that case.
135
	 */
L
Linus Torvalds 已提交
136
	unsigned long max_size, delta_size, base_size, src_copied, literal_added;
137
	unsigned long delta_limit;
138 139
	int score;

140 141 142 143 144 145 146
	/* We deal only with regular files.  Symlink renames are handled
	 * only when they are exact matches --- in other words, no edits
	 * after renaming.
	 */
	if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
		return 0;

147 148 149 150 151 152 153 154 155 156 157 158 159 160
	/*
	 * Need to check that source and destination sizes are
	 * filled in before comparing them.
	 *
	 * If we already have "cnt_data" filled in, we know it's
	 * all good (avoid checking the size for zero, as that
	 * is a possible size - we really should have a flag to
	 * say whether the size is valid or not!)
	 */
	if (!src->cnt_data && diff_populate_filespec(src, 0))
		return 0;
	if (!dst->cnt_data && diff_populate_filespec(dst, 0))
		return 0;

L
Linus Torvalds 已提交
161
	max_size = ((src->size > dst->size) ? src->size : dst->size);
162
	base_size = ((src->size < dst->size) ? src->size : dst->size);
L
Linus Torvalds 已提交
163
	delta_size = max_size - base_size;
164

165 166
	/* We would not consider edits that change the file size so
	 * drastically.  delta_size must be smaller than
167
	 * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
168
	 *
169 170 171
	 * Note that base_size == 0 case is handled here already
	 * and the final score computation below would not have a
	 * divide-by-zero issue.
172
	 */
173
	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
174 175
		return 0;

176 177
	delta_limit = (unsigned long)
		(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
178
	if (diffcore_count_changes(src, dst,
179
				   &src->cnt_data, &dst->cnt_data,
180 181
				   delta_limit,
				   &src_copied, &literal_added))
182
		return 0;
183

184 185
	/* How similar are they?
	 * what percentage of material in dst are from source?
186
	 */
L
Linus Torvalds 已提交
187
	if (!dst->size)
188
		score = 0; /* should not happen */
189
	else
190
		score = (int)(src_copied * MAX_SCORE / max_size);
191 192 193
	return score;
}

J
Junio C Hamano 已提交
194
static void record_rename_pair(int dst_index, int src_index, int score)
195
{
196
	struct diff_filespec *src, *dst;
197
	struct diff_filepair *dp;
198

199 200
	if (rename_dst[dst_index].pair)
		die("internal error: dst already matched.");
201

202
	src = rename_src[src_index].one;
203
	src->rename_used++;
204
	src->count++;
205

206
	dst = rename_dst[dst_index].two;
207
	dst->count++;
208

209
	dp = diff_queue(NULL, src, dst);
210
	dp->renamed_pair = 1;
211 212 213 214
	if (!strcmp(src->path, dst->path))
		dp->score = rename_src[src_index].score;
	else
		dp->score = score;
215
	rename_dst[dst_index].pair = dp;
216 217 218 219
}

/*
 * We sort the rename similarity matrix with the score, in descending
220
 * order (the most similar first).
221 222 223 224
 */
static int score_compare(const void *a_, const void *b_)
{
	const struct diff_score *a = a_, *b = b_;
225 226 227 228

	if (a->score == b->score)
		return b->name_score - a->name_score;

229 230 231
	return b->score - a->score;
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
struct file_similarity {
	int src_dst, index;
	struct diff_filespec *filespec;
	struct file_similarity *next;
};

static int find_identical_files(struct file_similarity *src,
				struct file_similarity *dst)
{
	int renames = 0;

	/*
	 * Walk over all the destinations ...
	 */
	do {
247
		struct diff_filespec *target = dst->filespec;
248
		struct file_similarity *p, *best;
249
		int i = 100, best_score = -1;
250 251 252 253 254 255

		/*
		 * .. to find the best source match
		 */
		best = NULL;
		for (p = src; p; p = p->next) {
256 257
			int score;
			struct diff_filespec *source = p->filespec;
258 259

			/* False hash collission? */
260
			if (hashcmp(source->sha1, target->sha1))
261 262
				continue;
			/* Non-regular files? If so, the modes must match! */
263 264
			if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
				if (source->mode != target->mode)
265 266
					continue;
			}
267 268 269 270 271 272 273 274 275
			/* Give higher scores to sources that haven't been used already */
			score = !source->rename_used;
			score += basename_same(source, target);
			if (score > best_score) {
				best = p;
				best_score = score;
				if (score == 2)
					break;
			}
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

			/* Too many identical alternatives? Pick one */
			if (!--i)
				break;
		}
		if (best) {
			record_rename_pair(dst->index, best->index, MAX_SCORE);
			renames++;
		}
	} while ((dst = dst->next) != NULL);
	return renames;
}

static void free_similarity_list(struct file_similarity *p)
{
	while (p) {
		struct file_similarity *entry = p;
		p = p->next;
		free(entry);
	}
}

static int find_same_files(void *ptr)
{
	int ret;
	struct file_similarity *p = ptr;
	struct file_similarity *src = NULL, *dst = NULL;

	/* Split the hash list up into sources and destinations */
	do {
		struct file_similarity *entry = p;
		p = p->next;
		if (entry->src_dst < 0) {
			entry->next = src;
			src = entry;
		} else {
			entry->next = dst;
			dst = entry;
		}
	} while (p);

	/*
	 * If we have both sources *and* destinations, see if
	 * we can match them up
	 */
	ret = (src && dst) ? find_identical_files(src, dst) : 0;

	/* Free the hashes and return the number of renames found */
	free_similarity_list(src);
	free_similarity_list(dst);
	return ret;
}

static unsigned int hash_filespec(struct diff_filespec *filespec)
{
	unsigned int hash;
	if (!filespec->sha1_valid) {
		if (diff_populate_filespec(filespec, 0))
			return 0;
		hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
	}
	memcpy(&hash, filespec->sha1, sizeof(hash));
	return hash;
}

static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec)
{
	void **pos;
	unsigned int hash;
	struct file_similarity *entry = xmalloc(sizeof(*entry));

	entry->src_dst = src_dst;
	entry->index = index;
	entry->filespec = filespec;
	entry->next = NULL;

	hash = hash_filespec(filespec);
	pos = insert_hash(hash, entry, table);

	/* We already had an entry there? */
	if (pos) {
		entry->next = *pos;
		*pos = entry;
	}
}

362 363 364 365 366 367 368 369 370
/*
 * Find exact renames first.
 *
 * The first round matches up the up-to-date entries,
 * and then during the second round we try to match
 * cache-dirty entries as well.
 */
static int find_exact_renames(void)
{
371 372
	int i;
	struct hash_table file_table;
373

374 375 376 377 378 379 380 381 382 383 384 385 386 387
	init_hash(&file_table);
	for (i = 0; i < rename_src_nr; i++)
		insert_file_table(&file_table, -1, i, rename_src[i].one);

	for (i = 0; i < rename_dst_nr; i++)
		insert_file_table(&file_table, 1, i, rename_dst[i].two);

	/* Find the renames */
	i = for_each_hash(&file_table, find_same_files);

	/* .. and free the hash data structure */
	free_hash(&file_table);

	return i;
388 389
}

390
void diffcore_rename(struct diff_options *options)
391
{
392 393 394
	int detect_rename = options->detect_rename;
	int minimum_score = options->rename_score;
	int rename_limit = options->rename_limit;
395
	struct diff_queue_struct *q = &diff_queued_diff;
J
Junio C Hamano 已提交
396
	struct diff_queue_struct outq;
397
	struct diff_score *mx;
398
	int i, j, rename_count;
399
	int num_create, num_src, dst_cnt;
400

401
	if (!minimum_score)
402
		minimum_score = DEFAULT_RENAME_SCORE;
403 404

	for (i = 0; i < q->nr; i++) {
405
		struct diff_filepair *p = q->queue[i];
406
		if (!DIFF_FILE_VALID(p->one)) {
407
			if (!DIFF_FILE_VALID(p->two))
408
				continue; /* unmerged */
409 410 411
			else if (options->single_follow &&
				 strcmp(options->single_follow, p->two->path))
				continue; /* not interested */
412
			else
413
				locate_rename_dst(p->two, 1);
414
		}
415
		else if (!DIFF_FILE_VALID(p->two)) {
416 417
			/*
			 * If the source is a broken "delete", and
418 419
			 * they did not really want to get broken,
			 * that means the source actually stays.
420 421 422 423 424 425 426 427 428 429 430
			 * So we increment the "rename_used" score
			 * by one, to indicate ourselves as a user
			 */
			if (p->broken_pair && !p->score)
				p->one->rename_used++;
			register_rename_src(p->one, p->score);
		}
		else if (detect_rename == DIFF_DETECT_COPY) {
			/*
			 * Increment the "rename_used" score by
			 * one, to indicate ourselves as a user.
431
			 */
432 433
			p->one->rename_used++;
			register_rename_src(p->one, p->score);
434
		}
435
	}
436
	if (rename_dst_nr == 0 || rename_src_nr == 0)
437 438
		goto cleanup; /* nothing to do */

439 440 441 442 443 444
	/*
	 * We really want to cull the candidates list early
	 * with cheap tests in order to avoid doing deltas.
	 */
	rename_count = find_exact_renames();

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
	/* Did we only want exact renames? */
	if (minimum_score == MAX_SCORE)
		goto cleanup;

	/*
	 * Calculate how many renames are left (but all the source
	 * files still remain as options for rename/copies!)
	 */
	num_create = (rename_dst_nr - rename_count);
	num_src = rename_src_nr;

	/* All done? */
	if (!num_create)
		goto cleanup;

460 461 462 463
	/*
	 * This basically does a test for the rename matrix not
	 * growing larger than a "rename_limit" square matrix, ie:
	 *
464
	 *    num_create * num_src > rename_limit * rename_limit
465 466 467 468 469 470
	 *
	 * but handles the potential overflow case specially (and we
	 * assume at least 32-bit integers)
	 */
	if (rename_limit <= 0 || rename_limit > 32767)
		rename_limit = 32767;
471
	if (num_create > rename_limit && num_src > rename_limit)
472
		goto cleanup;
473
	if (num_create * num_src > rename_limit * rename_limit)
474 475
		goto cleanup;

476
	mx = xmalloc(sizeof(*mx) * num_create * num_src);
477
	for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
478
		int base = dst_cnt * num_src;
479 480
		struct diff_filespec *two = rename_dst[i].two;
		if (rename_dst[i].pair)
481
			continue; /* dealt with exact match already. */
482 483 484 485 486 487 488
		for (j = 0; j < rename_src_nr; j++) {
			struct diff_filespec *one = rename_src[j].one;
			struct diff_score *m = &mx[base+j];
			m->src = j;
			m->dst = i;
			m->score = estimate_similarity(one, two,
						       minimum_score);
489
			m->name_score = basename_same(one, two);
490
			diff_free_filespec_blob(one);
491
		}
492
		/* We do not need the text anymore */
493
		diff_free_filespec_blob(two);
494 495 496 497
		dst_cnt++;
	}
	/* cost matrix sorted by most to least similar pair */
	qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
498 499 500 501 502 503 504 505 506 507 508 509 510
	for (i = 0; i < num_create * num_src; i++) {
		struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
		struct diff_filespec *src;
		if (dst->pair)
			continue; /* already done, either exact or fuzzy. */
		if (mx[i].score < minimum_score)
			break; /* there is no more usable pair. */
		src = rename_src[mx[i].src].one;
		if (src->rename_used)
			continue;
		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
		rename_count++;
	}
511
	for (i = 0; i < num_create * num_src; i++) {
512 513 514
		struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
		if (dst->pair)
			continue; /* already done, either exact or fuzzy. */
515
		if (mx[i].score < minimum_score)
516
			break; /* there is no more usable pair. */
J
Junio C Hamano 已提交
517 518
		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
		rename_count++;
519 520 521
	}
	free(mx);

522
 cleanup:
523
	/* At this point, we have found some renames and copies and they
J
Junio C Hamano 已提交
524
	 * are recorded in rename_dst.  The original list is still in *q.
525
	 */
526 527
	outq.queue = NULL;
	outq.nr = outq.alloc = 0;
528
	for (i = 0; i < q->nr; i++) {
529 530 531
		struct diff_filepair *p = q->queue[i];
		struct diff_filepair *pair_to_free = NULL;

532 533 534 535 536 537 538 539 540 541
		if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
			/*
			 * Creation
			 *
			 * We would output this create record if it has
			 * not been turned into a rename/copy already.
			 */
			struct diff_rename_dst *dst =
				locate_rename_dst(p->two, 0);
			if (dst && dst->pair) {
542 543 544 545
				diff_q(&outq, dst->pair);
				pair_to_free = p;
			}
			else
546 547
				/* no matching rename/copy source, so
				 * record this as a creation.
548 549
				 */
				diff_q(&outq, p);
550
		}
551 552 553 554
		else if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {
			/*
			 * Deletion
			 *
555 556 557 558
			 * We would output this delete record if:
			 *
			 * (1) this is a broken delete and the counterpart
			 *     broken create remains in the output; or
J
Junio C Hamano 已提交
559 560 561
			 * (2) this is not a broken delete, and rename_dst
			 *     does not have a rename/copy to move p->one->path
			 *     out of existence.
562 563 564 565 566
			 *
			 * Otherwise, the counterpart broken create
			 * has been turned into a rename-edit; or
			 * delete did not have a matching create to
			 * begin with.
567
			 */
568 569 570 571 572 573 574 575 576
			if (DIFF_PAIR_BROKEN(p)) {
				/* broken delete */
				struct diff_rename_dst *dst =
					locate_rename_dst(p->one, 0);
				if (dst && dst->pair)
					/* counterpart is now rename/copy */
					pair_to_free = p;
			}
			else {
577
				if (p->one->rename_used)
578 579 580
					/* this path remains */
					pair_to_free = p;
			}
581 582 583 584 585 586

			if (pair_to_free)
				;
			else
				diff_q(&outq, p);
		}
587
		else if (!diff_unmodified_pair(p))
588
			/* all the usual ones need to be kept */
589
			diff_q(&outq, p);
590 591 592 593
		else
			/* no need to keep unmodified pairs */
			pair_to_free = p;

594 595
		if (pair_to_free)
			diff_free_filepair(pair_to_free);
596
	}
597
	diff_debug_queue("done copying original", &outq);
598

599 600 601
	free(q->queue);
	*q = outq;
	diff_debug_queue("done collapsing", q);
602

603 604
	for (i = 0; i < rename_dst_nr; i++)
		free_filespec(rename_dst[i].two);
J
Junio C Hamano 已提交
605

606 607 608 609 610 611
	free(rename_dst);
	rename_dst = NULL;
	rename_dst_nr = rename_dst_alloc = 0;
	free(rename_src);
	rename_src = NULL;
	rename_src_nr = rename_src_alloc = 0;
612 613
	return;
}