diffcore-rename.c 15.1 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;

L
Linus Torvalds 已提交
147
	max_size = ((src->size > dst->size) ? src->size : dst->size);
148
	base_size = ((src->size < dst->size) ? src->size : dst->size);
L
Linus Torvalds 已提交
149
	delta_size = max_size - base_size;
150

151 152
	/* We would not consider edits that change the file size so
	 * drastically.  delta_size must be smaller than
153
	 * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
154
	 *
155 156 157
	 * Note that base_size == 0 case is handled here already
	 * and the final score computation below would not have a
	 * divide-by-zero issue.
158
	 */
159
	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
160 161
		return 0;

J
Jeff King 已提交
162 163
	if ((!src->cnt_data && diff_populate_filespec(src, 0))
		|| (!dst->cnt_data && diff_populate_filespec(dst, 0)))
164 165
		return 0; /* error but caught downstream */

166

167 168
	delta_limit = (unsigned long)
		(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
169
	if (diffcore_count_changes(src, dst,
170
				   &src->cnt_data, &dst->cnt_data,
171 172
				   delta_limit,
				   &src_copied, &literal_added))
173
		return 0;
174

175 176
	/* How similar are they?
	 * what percentage of material in dst are from source?
177
	 */
L
Linus Torvalds 已提交
178
	if (!dst->size)
179
		score = 0; /* should not happen */
180
	else
181
		score = (int)(src_copied * MAX_SCORE / max_size);
182 183 184
	return score;
}

J
Junio C Hamano 已提交
185
static void record_rename_pair(int dst_index, int src_index, int score)
186
{
187
	struct diff_filespec *src, *dst;
188
	struct diff_filepair *dp;
189

190 191
	if (rename_dst[dst_index].pair)
		die("internal error: dst already matched.");
192

193
	src = rename_src[src_index].one;
194
	src->rename_used++;
195
	src->count++;
196

197
	dst = rename_dst[dst_index].two;
198
	dst->count++;
199

200
	dp = diff_queue(NULL, src, dst);
201
	dp->renamed_pair = 1;
202 203 204 205
	if (!strcmp(src->path, dst->path))
		dp->score = rename_src[src_index].score;
	else
		dp->score = score;
206
	rename_dst[dst_index].pair = dp;
207 208 209 210
}

/*
 * We sort the rename similarity matrix with the score, in descending
211
 * order (the most similar first).
212 213 214 215
 */
static int score_compare(const void *a_, const void *b_)
{
	const struct diff_score *a = a_, *b = b_;
216 217 218 219

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

220 221 222
	return b->score - a->score;
}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 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 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
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 {
		struct diff_filespec *one = dst->filespec;
		struct file_similarity *p, *best;
		int i = 100;

		/*
		 * .. to find the best source match
		 */
		best = NULL;
		for (p = src; p; p = p->next) {
			struct diff_filespec *two = p->filespec;

			/* False hash collission? */
			if (hashcmp(one->sha1, two->sha1))
				continue;
			/* Non-regular files? If so, the modes must match! */
			if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
				if (one->mode != two->mode)
					continue;
			}
			best = p;
			if (basename_same(one, two))
				break;

			/* 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;
}

/*
 * Note: the rest of the rename logic depends on this
 * phase also populating all the filespecs for any
 * entry that isn't matched up with an exact rename.
 */
static void free_similarity_list(struct file_similarity *p)
{
	while (p) {
		struct file_similarity *entry = p;
		p = p->next;

		/* Stupid special case, see note above! */
		diff_populate_filespec(entry->filespec, 0);
		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;
	}
}

354 355 356 357 358 359 360 361 362
/*
 * 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)
{
363 364
	int i;
	struct hash_table file_table;
365

366 367 368 369 370 371 372 373 374 375 376 377 378 379
	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;
380 381
}

382
void diffcore_rename(struct diff_options *options)
383
{
384 385 386
	int detect_rename = options->detect_rename;
	int minimum_score = options->rename_score;
	int rename_limit = options->rename_limit;
387
	struct diff_queue_struct *q = &diff_queued_diff;
J
Junio C Hamano 已提交
388
	struct diff_queue_struct outq;
389
	struct diff_score *mx;
390
	int i, j, rename_count;
391
	int num_create, num_src, dst_cnt;
392

393
	if (!minimum_score)
394
		minimum_score = DEFAULT_RENAME_SCORE;
395 396

	for (i = 0; i < q->nr; i++) {
397
		struct diff_filepair *p = q->queue[i];
398
		if (!DIFF_FILE_VALID(p->one)) {
399
			if (!DIFF_FILE_VALID(p->two))
400
				continue; /* unmerged */
401 402 403
			else if (options->single_follow &&
				 strcmp(options->single_follow, p->two->path))
				continue; /* not interested */
404
			else
405
				locate_rename_dst(p->two, 1);
406
		}
407
		else if (!DIFF_FILE_VALID(p->two)) {
408 409
			/*
			 * If the source is a broken "delete", and
410 411
			 * they did not really want to get broken,
			 * that means the source actually stays.
412 413 414 415 416 417 418 419 420 421 422
			 * 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.
423
			 */
424 425
			p->one->rename_used++;
			register_rename_src(p->one, p->score);
426
		}
427
	}
428
	if (rename_dst_nr == 0 || rename_src_nr == 0)
429 430
		goto cleanup; /* nothing to do */

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	/*
	 * This basically does a test for the rename matrix not
	 * growing larger than a "rename_limit" square matrix, ie:
	 *
	 *    rename_dst_nr * rename_src_nr > rename_limit * rename_limit
	 *
	 * 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;
	if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit)
		goto cleanup;
	if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit)
		goto cleanup;

447 448
	/*
	 * We really want to cull the candidates list early
449 450
	 * with cheap tests in order to avoid doing deltas.
	 */
451
	rename_count = find_exact_renames();
452 453 454 455

	/* Have we run out the created file pool?  If so we can avoid
	 * doing the delta matrix altogether.
	 */
J
Junio C Hamano 已提交
456
	if (rename_count == rename_dst_nr)
457
		goto cleanup;
458

459 460 461
	if (minimum_score == MAX_SCORE)
		goto cleanup;

J
Junio C Hamano 已提交
462
	num_create = (rename_dst_nr - rename_count);
463
	num_src = rename_src_nr;
464
	mx = xmalloc(sizeof(*mx) * num_create * num_src);
465
	for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
466
		int base = dst_cnt * num_src;
467 468
		struct diff_filespec *two = rename_dst[i].two;
		if (rename_dst[i].pair)
469
			continue; /* dealt with exact match already. */
470 471 472 473 474 475 476
		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);
477
			m->name_score = basename_same(one, two);
478
			diff_free_filespec_blob(one);
479
		}
480
		/* We do not need the text anymore */
481
		diff_free_filespec_blob(two);
482 483 484 485 486
		dst_cnt++;
	}
	/* cost matrix sorted by most to least similar pair */
	qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
	for (i = 0; i < num_create * num_src; i++) {
487 488 489
		struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
		if (dst->pair)
			continue; /* already done, either exact or fuzzy. */
490
		if (mx[i].score < minimum_score)
491
			break; /* there is no more usable pair. */
J
Junio C Hamano 已提交
492 493
		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
		rename_count++;
494 495 496
	}
	free(mx);

497
 cleanup:
498
	/* At this point, we have found some renames and copies and they
J
Junio C Hamano 已提交
499
	 * are recorded in rename_dst.  The original list is still in *q.
500
	 */
501 502
	outq.queue = NULL;
	outq.nr = outq.alloc = 0;
503
	for (i = 0; i < q->nr; i++) {
504 505 506
		struct diff_filepair *p = q->queue[i];
		struct diff_filepair *pair_to_free = NULL;

507 508 509 510 511 512 513 514 515 516
		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) {
517 518 519 520
				diff_q(&outq, dst->pair);
				pair_to_free = p;
			}
			else
521 522
				/* no matching rename/copy source, so
				 * record this as a creation.
523 524
				 */
				diff_q(&outq, p);
525
		}
526 527 528 529
		else if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {
			/*
			 * Deletion
			 *
530 531 532 533
			 * 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 已提交
534 535 536
			 * (2) this is not a broken delete, and rename_dst
			 *     does not have a rename/copy to move p->one->path
			 *     out of existence.
537 538 539 540 541
			 *
			 * Otherwise, the counterpart broken create
			 * has been turned into a rename-edit; or
			 * delete did not have a matching create to
			 * begin with.
542
			 */
543 544 545 546 547 548 549 550 551
			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 {
552
				if (p->one->rename_used)
553 554 555
					/* this path remains */
					pair_to_free = p;
			}
556 557 558 559 560 561

			if (pair_to_free)
				;
			else
				diff_q(&outq, p);
		}
562
		else if (!diff_unmodified_pair(p))
563
			/* all the usual ones need to be kept */
564
			diff_q(&outq, p);
565 566 567 568
		else
			/* no need to keep unmodified pairs */
			pair_to_free = p;

569 570
		if (pair_to_free)
			diff_free_filepair(pair_to_free);
571
	}
572
	diff_debug_queue("done copying original", &outq);
573

574 575 576
	free(q->queue);
	*q = outq;
	diff_debug_queue("done collapsing", q);
577

578 579
	for (i = 0; i < rename_dst_nr; i++)
		free_filespec(rename_dst[i].two);
J
Junio C Hamano 已提交
580

581 582 583 584 585 586
	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;
587 588
	return;
}