diff options
Diffstat (limited to 'diffcore-rename.c')
| -rw-r--r-- | diffcore-rename.c | 69 |
1 files changed, 44 insertions, 25 deletions
diff --git a/diffcore-rename.c b/diffcore-rename.c index 749a35d2c2..7f03eb5a04 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -15,8 +15,7 @@ static struct diff_rename_dst { } *rename_dst; static int rename_dst_nr, rename_dst_alloc; -static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, - int insert_ok) +static int find_rename_dst(struct diff_filespec *two) { int first, last; @@ -27,16 +26,33 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, struct diff_rename_dst *dst = &(rename_dst[next]); int cmp = strcmp(two->path, dst->two->path); if (!cmp) - return dst; + return next; if (cmp < 0) { last = next; continue; } first = next+1; } - /* not found */ - if (!insert_ok) - return NULL; + return -first - 1; +} + +static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two) +{ + int ofs = find_rename_dst(two); + return ofs < 0 ? NULL : &rename_dst[ofs]; +} + +/* + * Returns 0 on success, -1 if we found a duplicate. + */ +static int add_rename_dst(struct diff_filespec *two) +{ + int first = find_rename_dst(two); + + if (first >= 0) + return -1; + first = -first - 1; + /* insert to make it at "first" */ ALLOC_GROW(rename_dst, rename_dst_nr + 1, rename_dst_alloc); rename_dst_nr++; @@ -46,7 +62,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, rename_dst[first].two = alloc_filespec(two->path); fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode); rename_dst[first].pair = NULL; - return &(rename_dst[first]); + return 0; } /* Table of rename/copy src files */ @@ -147,9 +163,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); @@ -242,14 +260,12 @@ struct file_similarity { 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; + return sha1hash(filespec->sha1); } static int find_identical_files(struct hashmap *srcs, @@ -259,15 +275,14 @@ static int find_identical_files(struct hashmap *srcs, int renames = 0; struct diff_filespec *target = rename_dst[dst_index].two; - struct file_similarity *p, *best, dst; + struct file_similarity *p, *best = NULL; int i = 100, best_score = -1; /* * Find the best source match for specified destination. */ - best = NULL; - hashmap_entry_init(&dst, hash_filespec(target)); - for (p = hashmap_get(srcs, &dst, NULL); p; p = hashmap_get_next(srcs, p)) { + 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; @@ -325,9 +340,11 @@ static int find_exact_renames(struct diff_options *options) int i, renames = 0; struct hashmap file_table; - /* Add all sources to the hash table */ + /* Add all sources to the hash table in reverse order, because + * later on they will be retrieved in LIFO order. + */ hashmap_init(&file_table, NULL, rename_src_nr); - for (i = 0; i < rename_src_nr; i++) + for (i = rename_src_nr-1; i >= 0; i--) insert_file_table(&file_table, i, rename_src[i].p->one); /* Walk the destinations and find best source match */ @@ -451,8 +468,12 @@ void diffcore_rename(struct diff_options *options) 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 (add_rename_dst(p->two) < 0) { + warning("skipping rename detection, detected" + " duplicate destination '%s'", + p->two->path); + goto cleanup; + } } else if (!DIFF_OPT_TST(options, RENAME_EMPTY) && is_empty_blob_sha1(p->one->sha1)) @@ -518,7 +539,7 @@ void diffcore_rename(struct diff_options *options) rename_dst_nr * rename_src_nr, 50, 1); } - mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx)); + mx = xcalloc(st_mult(num_create, NUM_CANDIDATE_PER_DST), sizeof(*mx)); for (dst_cnt = i = 0; i < rename_dst_nr; i++) { struct diff_filespec *two = rename_dst[i].two; struct diff_score *m; @@ -583,8 +604,7 @@ void diffcore_rename(struct diff_options *options) * 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); + struct diff_rename_dst *dst = locate_rename_dst(p->two); if (dst && dst->pair) { diff_q(&outq, dst->pair); pair_to_free = p; @@ -614,8 +634,7 @@ void diffcore_rename(struct diff_options *options) */ if (DIFF_PAIR_BROKEN(p)) { /* broken delete */ - struct diff_rename_dst *dst = - locate_rename_dst(p->one, 0); + struct diff_rename_dst *dst = locate_rename_dst(p->one); if (dst && dst->pair) /* counterpart is now rename/copy */ pair_to_free = p; |
