Add corner case tests for diff-index and diff-files
[gitweb.git] / diffcore-rename.c
index f9ebea56406090af207f79951618742dcd7d397f..31941bcbbf457fe02097f8c46d25f1f9fc7c4f9f 100644 (file)
@@ -244,28 +244,35 @@ static int find_identical_files(struct file_similarity *src,
         * Walk over all the destinations ...
         */
        do {
-               struct diff_filespec *one = dst->filespec;
+               struct diff_filespec *target = dst->filespec;
                struct file_similarity *p, *best;
-               int i = 100;
+               int i = 100, best_score = -1;
 
                /*
                 * .. to find the best source match
                 */
                best = NULL;
                for (p = src; p; p = p->next) {
-                       struct diff_filespec *two = p->filespec;
+                       int score;
+                       struct diff_filespec *source = p->filespec;
 
                        /* False hash collission? */
-                       if (hashcmp(one->sha1, two->sha1))
+                       if (hashcmp(source->sha1, target->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)
+                       if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
+                               if (source->mode != target->mode)
                                        continue;
                        }
-                       best = p;
-                       if (basename_same(one, two))
-                               break;
+                       /* 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;
+                       }
 
                        /* Too many identical alternatives? Pick one */
                        if (!--i)
@@ -461,10 +468,11 @@ void diffcore_rename(struct diff_options *options)
         */
        if (rename_limit <= 0 || rename_limit > 32767)
                rename_limit = 32767;
-       if (num_create > rename_limit && num_src > rename_limit)
-               goto cleanup;
-       if (num_create * num_src > rename_limit * rename_limit)
+       if ((num_create > rename_limit && num_src > rename_limit) ||
+           (num_create * num_src > rename_limit * rename_limit)) {
+               warning("too many files, skipping inexact rename detection");
                goto cleanup;
+       }
 
        mx = xmalloc(sizeof(*mx) * num_create * num_src);
        for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
@@ -488,6 +496,19 @@ void diffcore_rename(struct diff_options *options)
        }
        /* 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++) {
+               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++;
+       }
        for (i = 0; i < num_create * num_src; i++) {
                struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
                if (dst->pair)