diff options
Diffstat (limited to 'diffcore-rename.c')
-rw-r--r-- | diffcore-rename.c | 321 |
1 files changed, 155 insertions, 166 deletions
diff --git a/diffcore-rename.c b/diffcore-rename.c index d40e40a3ac..4e132f1fdb 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -4,7 +4,7 @@ #include "cache.h" #include "diff.h" #include "diffcore.h" -#include "hash.h" +#include "hashmap.h" #include "progress.h" /* Table of rename/copy destinations */ @@ -38,39 +38,36 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, 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)); - } + ALLOC_GROW(rename_dst, rename_dst_nr + 1, rename_dst_alloc); rename_dst_nr++; if (first < rename_dst_nr) memmove(rename_dst + first + 1, rename_dst + first, (rename_dst_nr - first - 1) * sizeof(*rename_dst)); rename_dst[first].two = alloc_filespec(two->path); - fill_filespec(rename_dst[first].two, two->sha1, two->mode); + fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode); rename_dst[first].pair = NULL; return &(rename_dst[first]); } /* Table of rename/copy src files */ static struct diff_rename_src { - struct diff_filespec *one; + struct diff_filepair *p; unsigned short score; /* to remember the break score */ } *rename_src; static int rename_src_nr, rename_src_alloc; -static struct diff_rename_src *register_rename_src(struct diff_filespec *one, - unsigned short score) +static struct diff_rename_src *register_rename_src(struct diff_filepair *p) { int first, last; + struct diff_filespec *one = p->one; + unsigned short score = p->score; 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); + int cmp = strcmp(one->path, src->p->one->path); if (!cmp) return src; if (cmp < 0) { @@ -81,16 +78,12 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one, } /* 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)); - } + ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc); 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; + rename_src[first].p = p; rename_src[first].score = score; return &(rename_src[first]); } @@ -154,9 +147,11 @@ static int estimate_similarity(struct diff_filespec *src, * 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, 1)) + if (!src->cnt_data && + diff_populate_filespec(src, CHECK_SIZE_ONLY)) return 0; - if (!dst->cnt_data && diff_populate_filespec(dst, 1)) + if (!dst->cnt_data && + diff_populate_filespec(dst, CHECK_SIZE_ONLY)) return 0; max_size = ((src->size > dst->size) ? src->size : dst->size); @@ -205,7 +200,7 @@ static void record_rename_pair(int dst_index, int src_index, int score) if (rename_dst[dst_index].pair) die("internal error: dst already matched."); - src = rename_src[src_index].one; + src = rename_src[src_index].p->one; src->rename_used++; src->count++; @@ -242,137 +237,79 @@ static int score_compare(const void *a_, const void *b_) } struct file_similarity { - int src_dst, index; + struct hashmap_entry entry; + int index; struct diff_filespec *filespec; - struct file_similarity *next; }; -static int find_identical_files(struct file_similarity *src, - struct file_similarity *dst, +static unsigned int hash_filespec(struct diff_filespec *filespec) +{ + if (!filespec->sha1_valid) { + if (diff_populate_filespec(filespec, 0)) + return 0; + hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1); + } + return sha1hash(filespec->sha1); +} + +static int find_identical_files(struct hashmap *srcs, + int dst_index, struct diff_options *options) { int renames = 0; + struct diff_filespec *target = rename_dst[dst_index].two; + struct file_similarity *p, *best = NULL; + int i = 100, best_score = -1; + /* - * Walk over all the destinations ... + * Find the best source match for specified destination. */ - do { - struct diff_filespec *target = dst->filespec; - struct file_similarity *p, *best; - int i = 100, best_score = -1; - - /* - * .. to find the best source match - */ - best = NULL; - for (p = src; p; p = p->next) { - int score; - struct diff_filespec *source = p->filespec; - - /* False hash collision? */ - if (hashcmp(source->sha1, target->sha1)) - continue; - /* Non-regular files? If so, the modes must match! */ - if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) { - if (source->mode != target->mode) - continue; - } - /* Give higher scores to sources that haven't been used already */ - score = !source->rename_used; - if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY) - continue; - score += basename_same(source, target); - if (score > best_score) { - best = p; - best_score = score; - if (score == 2) - break; - } + p = hashmap_get_from_hash(srcs, hash_filespec(target), NULL); + for (; p; p = hashmap_get_next(srcs, p)) { + int score; + struct diff_filespec *source = p->filespec; - /* Too many identical alternatives? Pick one */ - if (!--i) - break; + /* False hash collision? */ + if (hashcmp(source->sha1, target->sha1)) + continue; + /* Non-regular files? If so, the modes must match! */ + if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) { + if (source->mode != target->mode) + continue; } - if (best) { - record_rename_pair(dst->index, best->index, MAX_SCORE); - renames++; + /* Give higher scores to sources that haven't been used already */ + score = !source->rename_used; + if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY) + continue; + score += basename_same(source, target); + if (score > best_score) { + best = p; + best_score = score; + if (score == 2) + break; } - } 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); + /* Too many identical alternatives? Pick one */ + if (!--i) + break; } -} - -static int find_same_files(void *ptr, void *data) -{ - int ret; - struct file_similarity *p = ptr; - struct file_similarity *src = NULL, *dst = NULL; - struct diff_options *options = data; - - /* 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, options) : 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); + if (best) { + record_rename_pair(dst_index, best->index, MAX_SCORE); + renames++; } - memcpy(&hash, filespec->sha1, sizeof(hash)); - return hash; + return renames; } -static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec) +static void insert_file_table(struct hashmap *table, 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; - } + hashmap_entry_init(entry, hash_filespec(filespec)); + hashmap_add(table, entry); } /* @@ -384,23 +321,22 @@ static void insert_file_table(struct hash_table *table, int src_dst, int index, */ static int find_exact_renames(struct diff_options *options) { - int i; - struct hash_table file_table; + int i, renames = 0; + struct hashmap file_table; - init_hash(&file_table); + /* Add all sources to the hash table */ + hashmap_init(&file_table, NULL, rename_src_nr); for (i = 0; i < rename_src_nr; i++) - insert_file_table(&file_table, -1, i, rename_src[i].one); + insert_file_table(&file_table, i, rename_src[i].p->one); + /* Walk the destinations and find best source match */ 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, options); + renames += find_identical_files(&file_table, i, options); - /* .. and free the hash data structure */ - free_hash(&file_table); + /* Free the hash data structure and entries */ + hashmap_free(&file_table, 1); - return i; + return renames; } #define NUM_CANDIDATE_PER_DST 4 @@ -419,6 +355,55 @@ static void record_if_better(struct diff_score m[], struct diff_score *o) m[worst] = *o; } +/* + * Returns: + * 0 if we are under the limit; + * 1 if we need to disable inexact rename detection; + * 2 if we would be under the limit if we were given -C instead of -C -C. + */ +static int too_many_rename_candidates(int num_create, + struct diff_options *options) +{ + int rename_limit = options->rename_limit; + int num_src = rename_src_nr; + int i; + + options->needed_rename_limit = 0; + + /* + * This basically does a test for the rename matrix not + * growing larger than a "rename_limit" square matrix, ie: + * + * num_create * num_src > 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 ((num_create <= rename_limit || num_src <= rename_limit) && + (num_create * num_src <= rename_limit * rename_limit)) + return 0; + + options->needed_rename_limit = + num_src > num_create ? num_src : num_create; + + /* Are we running under -C -C? */ + if (!DIFF_OPT_TST(options, FIND_COPIES_HARDER)) + return 1; + + /* Would we bust the limit if we were running under -C? */ + for (num_src = i = 0; i < rename_src_nr; i++) { + if (diff_unmodified_pair(rename_src[i].p)) + continue; + num_src++; + } + if ((num_create <= rename_limit || num_src <= rename_limit) && + (num_create * num_src <= rename_limit * rename_limit)) + return 2; + return 1; +} + static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, int copies) { int count = 0, i; @@ -432,7 +417,7 @@ static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, i dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (!copies && rename_src[mx[i].src].one->rename_used) + if (!copies && rename_src[mx[i].src].p->one->rename_used) continue; record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); count++; @@ -444,12 +429,11 @@ void diffcore_rename(struct diff_options *options) { int detect_rename = options->detect_rename; int minimum_score = options->rename_score; - int rename_limit = options->rename_limit; struct diff_queue_struct *q = &diff_queued_diff; struct diff_queue_struct outq; struct diff_score *mx; - int i, j, rename_count; - int num_create, num_src, dst_cnt; + int i, j, rename_count, skip_unmodified = 0; + int num_create, dst_cnt; struct progress *progress = NULL; if (!minimum_score) @@ -463,10 +447,16 @@ void diffcore_rename(struct diff_options *options) else if (options->single_follow && strcmp(options->single_follow, p->two->path)) continue; /* not interested */ + else if (!DIFF_OPT_TST(options, RENAME_EMPTY) && + is_empty_blob_sha1(p->two->sha1)) + continue; else locate_rename_dst(p->two, 1); } - else if (!DIFF_FILE_VALID(p->two)) { + else if (!DIFF_OPT_TST(options, RENAME_EMPTY) && + is_empty_blob_sha1(p->one->sha1)) + continue; + else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) { /* * If the source is a broken "delete", and * they did not really want to get broken, @@ -476,7 +466,7 @@ void diffcore_rename(struct diff_options *options) */ if (p->broken_pair && !p->score) p->one->rename_used++; - register_rename_src(p->one, p->score); + register_rename_src(p); } else if (detect_rename == DIFF_DETECT_COPY) { /* @@ -484,7 +474,7 @@ void diffcore_rename(struct diff_options *options) * one, to indicate ourselves as a user. */ p->one->rename_used++; - register_rename_src(p->one, p->score); + register_rename_src(p); } } if (rename_dst_nr == 0 || rename_src_nr == 0) @@ -505,34 +495,25 @@ void diffcore_rename(struct diff_options *options) * 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; - /* - * This basically does a test for the rename matrix not - * growing larger than a "rename_limit" square matrix, ie: - * - * num_create * num_src > rename_limit * rename_limit - * - * but handles the potential overflow case specially (and we - * assume at least 32-bit integers) - */ - options->needed_rename_limit = 0; - if (rename_limit <= 0 || rename_limit > 32767) - rename_limit = 32767; - if ((num_create > rename_limit && num_src > rename_limit) || - (num_create * num_src > rename_limit * rename_limit)) { - options->needed_rename_limit = - num_src > num_create ? num_src : num_create; + switch (too_many_rename_candidates(num_create, options)) { + case 1: goto cleanup; + case 2: + options->degraded_cc_to_c = 1; + skip_unmodified = 1; + break; + default: + break; } if (options->show_rename_progress) { progress = start_progress_delay( - "Performing inexact rename detection", + _("Performing inexact rename detection"), rename_dst_nr * rename_src_nr, 50, 1); } @@ -549,8 +530,13 @@ void diffcore_rename(struct diff_options *options) m[j].dst = -1; for (j = 0; j < rename_src_nr; j++) { - struct diff_filespec *one = rename_src[j].one; + struct diff_filespec *one = rename_src[j].p->one; struct diff_score this_src; + + if (skip_unmodified && + diff_unmodified_pair(rename_src[j].p)) + continue; + this_src.score = estimate_similarity(one, two, minimum_score); this_src.name_score = basename_same(one, two); @@ -586,7 +572,10 @@ void diffcore_rename(struct diff_options *options) struct diff_filepair *p = q->queue[i]; struct diff_filepair *pair_to_free = NULL; - if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { + if (DIFF_PAIR_UNMERGED(p)) { + diff_q(&outq, p); + } + else if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { /* * Creation * |