diffcore-rename.con commit Fix output of "git log --graph --boundary" (3c68d67)
   1/*
   2 * Copyright (C) 2005 Junio C Hamano
   3 */
   4#include "cache.h"
   5#include "diff.h"
   6#include "diffcore.h"
   7#include "hash.h"
   8
   9/* Table of rename/copy destinations */
  10
  11static struct diff_rename_dst {
  12        struct diff_filespec *two;
  13        struct diff_filepair *pair;
  14} *rename_dst;
  15static int rename_dst_nr, rename_dst_alloc;
  16
  17static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
  18                                                 int insert_ok)
  19{
  20        int first, last;
  21
  22        first = 0;
  23        last = rename_dst_nr;
  24        while (last > first) {
  25                int next = (last + first) >> 1;
  26                struct diff_rename_dst *dst = &(rename_dst[next]);
  27                int cmp = strcmp(two->path, dst->two->path);
  28                if (!cmp)
  29                        return dst;
  30                if (cmp < 0) {
  31                        last = next;
  32                        continue;
  33                }
  34                first = next+1;
  35        }
  36        /* not found */
  37        if (!insert_ok)
  38                return NULL;
  39        /* insert to make it at "first" */
  40        if (rename_dst_alloc <= rename_dst_nr) {
  41                rename_dst_alloc = alloc_nr(rename_dst_alloc);
  42                rename_dst = xrealloc(rename_dst,
  43                                      rename_dst_alloc * sizeof(*rename_dst));
  44        }
  45        rename_dst_nr++;
  46        if (first < rename_dst_nr)
  47                memmove(rename_dst + first + 1, rename_dst + first,
  48                        (rename_dst_nr - first - 1) * sizeof(*rename_dst));
  49        rename_dst[first].two = alloc_filespec(two->path);
  50        fill_filespec(rename_dst[first].two, two->sha1, two->mode);
  51        rename_dst[first].pair = NULL;
  52        return &(rename_dst[first]);
  53}
  54
  55/* Table of rename/copy src files */
  56static struct diff_rename_src {
  57        struct diff_filespec *one;
  58        unsigned short score; /* to remember the break score */
  59} *rename_src;
  60static int rename_src_nr, rename_src_alloc;
  61
  62static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
  63                                                   unsigned short score)
  64{
  65        int first, last;
  66
  67        first = 0;
  68        last = rename_src_nr;
  69        while (last > first) {
  70                int next = (last + first) >> 1;
  71                struct diff_rename_src *src = &(rename_src[next]);
  72                int cmp = strcmp(one->path, src->one->path);
  73                if (!cmp)
  74                        return src;
  75                if (cmp < 0) {
  76                        last = next;
  77                        continue;
  78                }
  79                first = next+1;
  80        }
  81
  82        /* insert to make it at "first" */
  83        if (rename_src_alloc <= rename_src_nr) {
  84                rename_src_alloc = alloc_nr(rename_src_alloc);
  85                rename_src = xrealloc(rename_src,
  86                                      rename_src_alloc * sizeof(*rename_src));
  87        }
  88        rename_src_nr++;
  89        if (first < rename_src_nr)
  90                memmove(rename_src + first + 1, rename_src + first,
  91                        (rename_src_nr - first - 1) * sizeof(*rename_src));
  92        rename_src[first].one = one;
  93        rename_src[first].score = score;
  94        return &(rename_src[first]);
  95}
  96
  97static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
  98{
  99        int src_len = strlen(src->path), dst_len = strlen(dst->path);
 100        while (src_len && dst_len) {
 101                char c1 = src->path[--src_len];
 102                char c2 = dst->path[--dst_len];
 103                if (c1 != c2)
 104                        return 0;
 105                if (c1 == '/')
 106                        return 1;
 107        }
 108        return (!src_len || src->path[src_len - 1] == '/') &&
 109                (!dst_len || dst->path[dst_len - 1] == '/');
 110}
 111
 112struct diff_score {
 113        int src; /* index in rename_src */
 114        int dst; /* index in rename_dst */
 115        unsigned short score;
 116        short name_score;
 117};
 118
 119static int estimate_similarity(struct diff_filespec *src,
 120                               struct diff_filespec *dst,
 121                               int minimum_score)
 122{
 123        /* src points at a file that existed in the original tree (or
 124         * optionally a file in the destination tree) and dst points
 125         * at a newly created file.  They may be quite similar, in which
 126         * case we want to say src is renamed to dst or src is copied into
 127         * dst, and then some edit has been applied to dst.
 128         *
 129         * Compare them and return how similar they are, representing
 130         * the score as an integer between 0 and MAX_SCORE.
 131         *
 132         * When there is an exact match, it is considered a better
 133         * match than anything else; the destination does not even
 134         * call into this function in that case.
 135         */
 136        unsigned long max_size, delta_size, base_size, src_copied, literal_added;
 137        unsigned long delta_limit;
 138        int score;
 139
 140        /* We deal only with regular files.  Symlink renames are handled
 141         * only when they are exact matches --- in other words, no edits
 142         * after renaming.
 143         */
 144        if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
 145                return 0;
 146
 147        /*
 148         * Need to check that source and destination sizes are
 149         * filled in before comparing them.
 150         *
 151         * If we already have "cnt_data" filled in, we know it's
 152         * all good (avoid checking the size for zero, as that
 153         * is a possible size - we really should have a flag to
 154         * say whether the size is valid or not!)
 155         */
 156        if (!src->cnt_data && diff_populate_filespec(src, 0))
 157                return 0;
 158        if (!dst->cnt_data && diff_populate_filespec(dst, 0))
 159                return 0;
 160
 161        max_size = ((src->size > dst->size) ? src->size : dst->size);
 162        base_size = ((src->size < dst->size) ? src->size : dst->size);
 163        delta_size = max_size - base_size;
 164
 165        /* We would not consider edits that change the file size so
 166         * drastically.  delta_size must be smaller than
 167         * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
 168         *
 169         * Note that base_size == 0 case is handled here already
 170         * and the final score computation below would not have a
 171         * divide-by-zero issue.
 172         */
 173        if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 174                return 0;
 175
 176        delta_limit = (unsigned long)
 177                (base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
 178        if (diffcore_count_changes(src, dst,
 179                                   &src->cnt_data, &dst->cnt_data,
 180                                   delta_limit,
 181                                   &src_copied, &literal_added))
 182                return 0;
 183
 184        /* How similar are they?
 185         * what percentage of material in dst are from source?
 186         */
 187        if (!dst->size)
 188                score = 0; /* should not happen */
 189        else
 190                score = (int)(src_copied * MAX_SCORE / max_size);
 191        return score;
 192}
 193
 194static 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        if (rename_dst[dst_index].pair)
 200                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        if (!strcmp(src->path, dst->path))
 212                dp->score = rename_src[src_index].score;
 213        else
 214                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 */
 222static int score_compare(const void *a_, const void *b_)
 223{
 224        const struct diff_score *a = a_, *b = b_;
 225
 226        /* sink the unused ones to the bottom */
 227        if (a->dst < 0)
 228                return (0 <= b->dst);
 229        else if (b->dst < 0)
 230                return -1;
 231
 232        if (a->score == b->score)
 233                return b->name_score - a->name_score;
 234
 235        return b->score - a->score;
 236}
 237
 238struct file_similarity {
 239        int src_dst, index;
 240        struct diff_filespec *filespec;
 241        struct file_similarity *next;
 242};
 243
 244static int find_identical_files(struct file_similarity *src,
 245                                struct file_similarity *dst)
 246{
 247        int renames = 0;
 248
 249        /*
 250         * Walk over all the destinations ...
 251         */
 252        do {
 253                struct diff_filespec *target = dst->filespec;
 254                struct file_similarity *p, *best;
 255                int i = 100, best_score = -1;
 256
 257                /*
 258                 * .. to find the best source match
 259                 */
 260                best = NULL;
 261                for (p = src; p; p = p->next) {
 262                        int score;
 263                        struct diff_filespec *source = p->filespec;
 264
 265                        /* False hash collission? */
 266                        if (hashcmp(source->sha1, target->sha1))
 267                                continue;
 268                        /* Non-regular files? If so, the modes must match! */
 269                        if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
 270                                if (source->mode != target->mode)
 271                                        continue;
 272                        }
 273                        /* Give higher scores to sources that haven't been used already */
 274                        score = !source->rename_used;
 275                        score += basename_same(source, target);
 276                        if (score > best_score) {
 277                                best = p;
 278                                best_score = score;
 279                                if (score == 2)
 280                                        break;
 281                        }
 282
 283                        /* Too many identical alternatives? Pick one */
 284                        if (!--i)
 285                                break;
 286                }
 287                if (best) {
 288                        record_rename_pair(dst->index, best->index, MAX_SCORE);
 289                        renames++;
 290                }
 291        } while ((dst = dst->next) != NULL);
 292        return renames;
 293}
 294
 295static void free_similarity_list(struct file_similarity *p)
 296{
 297        while (p) {
 298                struct file_similarity *entry = p;
 299                p = p->next;
 300                free(entry);
 301        }
 302}
 303
 304static int find_same_files(void *ptr)
 305{
 306        int ret;
 307        struct file_similarity *p = ptr;
 308        struct file_similarity *src = NULL, *dst = NULL;
 309
 310        /* Split the hash list up into sources and destinations */
 311        do {
 312                struct file_similarity *entry = p;
 313                p = p->next;
 314                if (entry->src_dst < 0) {
 315                        entry->next = src;
 316                        src = entry;
 317                } else {
 318                        entry->next = dst;
 319                        dst = entry;
 320                }
 321        } while (p);
 322
 323        /*
 324         * If we have both sources *and* destinations, see if
 325         * we can match them up
 326         */
 327        ret = (src && dst) ? find_identical_files(src, dst) : 0;
 328
 329        /* Free the hashes and return the number of renames found */
 330        free_similarity_list(src);
 331        free_similarity_list(dst);
 332        return ret;
 333}
 334
 335static unsigned int hash_filespec(struct diff_filespec *filespec)
 336{
 337        unsigned int hash;
 338        if (!filespec->sha1_valid) {
 339                if (diff_populate_filespec(filespec, 0))
 340                        return 0;
 341                hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
 342        }
 343        memcpy(&hash, filespec->sha1, sizeof(hash));
 344        return hash;
 345}
 346
 347static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec)
 348{
 349        void **pos;
 350        unsigned int hash;
 351        struct file_similarity *entry = xmalloc(sizeof(*entry));
 352
 353        entry->src_dst = src_dst;
 354        entry->index = index;
 355        entry->filespec = filespec;
 356        entry->next = NULL;
 357
 358        hash = hash_filespec(filespec);
 359        pos = insert_hash(hash, entry, table);
 360
 361        /* We already had an entry there? */
 362        if (pos) {
 363                entry->next = *pos;
 364                *pos = entry;
 365        }
 366}
 367
 368/*
 369 * Find exact renames first.
 370 *
 371 * The first round matches up the up-to-date entries,
 372 * and then during the second round we try to match
 373 * cache-dirty entries as well.
 374 */
 375static int find_exact_renames(void)
 376{
 377        int i;
 378        struct hash_table file_table;
 379
 380        init_hash(&file_table);
 381        for (i = 0; i < rename_src_nr; i++)
 382                insert_file_table(&file_table, -1, i, rename_src[i].one);
 383
 384        for (i = 0; i < rename_dst_nr; i++)
 385                insert_file_table(&file_table, 1, i, rename_dst[i].two);
 386
 387        /* Find the renames */
 388        i = for_each_hash(&file_table, find_same_files);
 389
 390        /* .. and free the hash data structure */
 391        free_hash(&file_table);
 392
 393        return i;
 394}
 395
 396#define NUM_CANDIDATE_PER_DST 4
 397static void record_if_better(struct diff_score m[], struct diff_score *o)
 398{
 399        int i, worst;
 400
 401        /* find the worst one */
 402        worst = 0;
 403        for (i = 1; i < NUM_CANDIDATE_PER_DST; i++)
 404                if (score_compare(&m[i], &m[worst]) > 0)
 405                        worst = i;
 406
 407        /* is it better than the worst one? */
 408        if (score_compare(&m[worst], o) > 0)
 409                m[worst] = *o;
 410}
 411
 412void diffcore_rename(struct diff_options *options)
 413{
 414        int detect_rename = options->detect_rename;
 415        int minimum_score = options->rename_score;
 416        int rename_limit = options->rename_limit;
 417        struct diff_queue_struct *q = &diff_queued_diff;
 418        struct diff_queue_struct outq;
 419        struct diff_score *mx;
 420        int i, j, rename_count;
 421        int num_create, num_src, dst_cnt;
 422
 423        if (!minimum_score)
 424                minimum_score = DEFAULT_RENAME_SCORE;
 425
 426        for (i = 0; i < q->nr; i++) {
 427                struct diff_filepair *p = q->queue[i];
 428                if (!DIFF_FILE_VALID(p->one)) {
 429                        if (!DIFF_FILE_VALID(p->two))
 430                                continue; /* unmerged */
 431                        else if (options->single_follow &&
 432                                 strcmp(options->single_follow, p->two->path))
 433                                continue; /* not interested */
 434                        else
 435                                locate_rename_dst(p->two, 1);
 436                }
 437                else if (!DIFF_FILE_VALID(p->two)) {
 438                        /*
 439                         * If the source is a broken "delete", and
 440                         * they did not really want to get broken,
 441                         * that means the source actually stays.
 442                         * So we increment the "rename_used" score
 443                         * by one, to indicate ourselves as a user
 444                         */
 445                        if (p->broken_pair && !p->score)
 446                                p->one->rename_used++;
 447                        register_rename_src(p->one, p->score);
 448                }
 449                else if (detect_rename == DIFF_DETECT_COPY) {
 450                        /*
 451                         * Increment the "rename_used" score by
 452                         * one, to indicate ourselves as a user.
 453                         */
 454                        p->one->rename_used++;
 455                        register_rename_src(p->one, p->score);
 456                }
 457        }
 458        if (rename_dst_nr == 0 || rename_src_nr == 0)
 459                goto cleanup; /* nothing to do */
 460
 461        /*
 462         * We really want to cull the candidates list early
 463         * with cheap tests in order to avoid doing deltas.
 464         */
 465        rename_count = find_exact_renames();
 466
 467        /* Did we only want exact renames? */
 468        if (minimum_score == MAX_SCORE)
 469                goto cleanup;
 470
 471        /*
 472         * Calculate how many renames are left (but all the source
 473         * files still remain as options for rename/copies!)
 474         */
 475        num_create = (rename_dst_nr - rename_count);
 476        num_src = rename_src_nr;
 477
 478        /* All done? */
 479        if (!num_create)
 480                goto cleanup;
 481
 482        /*
 483         * This basically does a test for the rename matrix not
 484         * growing larger than a "rename_limit" square matrix, ie:
 485         *
 486         *    num_create * num_src > rename_limit * rename_limit
 487         *
 488         * but handles the potential overflow case specially (and we
 489         * assume at least 32-bit integers)
 490         */
 491        if (rename_limit <= 0 || rename_limit > 32767)
 492                rename_limit = 32767;
 493        if ((num_create > rename_limit && num_src > rename_limit) ||
 494            (num_create * num_src > rename_limit * rename_limit)) {
 495                if (options->warn_on_too_large_rename)
 496                        warning("too many files, skipping inexact rename detection");
 497                goto cleanup;
 498        }
 499
 500        mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx));
 501        for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
 502                struct diff_filespec *two = rename_dst[i].two;
 503                struct diff_score *m;
 504
 505                if (rename_dst[i].pair)
 506                        continue; /* dealt with exact match already. */
 507
 508                m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST];
 509                for (j = 0; j < NUM_CANDIDATE_PER_DST; j++)
 510                        m[j].dst = -1;
 511
 512                for (j = 0; j < rename_src_nr; j++) {
 513                        struct diff_filespec *one = rename_src[j].one;
 514                        struct diff_score this_src;
 515                        this_src.score = estimate_similarity(one, two,
 516                                                             minimum_score);
 517                        this_src.name_score = basename_same(one, two);
 518                        this_src.dst = i;
 519                        this_src.src = j;
 520                        record_if_better(m, &this_src);
 521                        diff_free_filespec_blob(one);
 522                }
 523                /* We do not need the text anymore */
 524                diff_free_filespec_blob(two);
 525                dst_cnt++;
 526        }
 527
 528        /* cost matrix sorted by most to least similar pair */
 529        qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare);
 530
 531        for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
 532                struct diff_rename_dst *dst;
 533
 534                if ((mx[i].dst < 0) ||
 535                    (mx[i].score < minimum_score))
 536                        break; /* there is no more usable pair. */
 537                dst = &rename_dst[mx[i].dst];
 538                if (dst->pair)
 539                        continue; /* already done, either exact or fuzzy. */
 540                if (rename_src[mx[i].src].one->rename_used)
 541                        continue;
 542                record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
 543                rename_count++;
 544        }
 545
 546        for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
 547                struct diff_rename_dst *dst;
 548
 549                if ((mx[i].dst < 0) ||
 550                    (mx[i].score < minimum_score))
 551                        break; /* there is no more usable pair. */
 552                dst = &rename_dst[mx[i].dst];
 553                if (dst->pair)
 554                        continue; /* already done, either exact or fuzzy. */
 555                record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
 556                rename_count++;
 557        }
 558        free(mx);
 559
 560 cleanup:
 561        /* At this point, we have found some renames and copies and they
 562         * are recorded in rename_dst.  The original list is still in *q.
 563         */
 564        outq.queue = NULL;
 565        outq.nr = outq.alloc = 0;
 566        for (i = 0; i < q->nr; i++) {
 567                struct diff_filepair *p = q->queue[i];
 568                struct diff_filepair *pair_to_free = NULL;
 569
 570                if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
 571                        /*
 572                         * Creation
 573                         *
 574                         * We would output this create record if it has
 575                         * not been turned into a rename/copy already.
 576                         */
 577                        struct diff_rename_dst *dst =
 578                                locate_rename_dst(p->two, 0);
 579                        if (dst && dst->pair) {
 580                                diff_q(&outq, dst->pair);
 581                                pair_to_free = p;
 582                        }
 583                        else
 584                                /* no matching rename/copy source, so
 585                                 * record this as a creation.
 586                                 */
 587                                diff_q(&outq, p);
 588                }
 589                else if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {
 590                        /*
 591                         * Deletion
 592                         *
 593                         * We would output this delete record if:
 594                         *
 595                         * (1) this is a broken delete and the counterpart
 596                         *     broken create remains in the output; or
 597                         * (2) this is not a broken delete, and rename_dst
 598                         *     does not have a rename/copy to move p->one->path
 599                         *     out of existence.
 600                         *
 601                         * Otherwise, the counterpart broken create
 602                         * has been turned into a rename-edit; or
 603                         * delete did not have a matching create to
 604                         * begin with.
 605                         */
 606                        if (DIFF_PAIR_BROKEN(p)) {
 607                                /* broken delete */
 608                                struct diff_rename_dst *dst =
 609                                        locate_rename_dst(p->one, 0);
 610                                if (dst && dst->pair)
 611                                        /* counterpart is now rename/copy */
 612                                        pair_to_free = p;
 613                        }
 614                        else {
 615                                if (p->one->rename_used)
 616                                        /* this path remains */
 617                                        pair_to_free = p;
 618                        }
 619
 620                        if (pair_to_free)
 621                                ;
 622                        else
 623                                diff_q(&outq, p);
 624                }
 625                else if (!diff_unmodified_pair(p))
 626                        /* all the usual ones need to be kept */
 627                        diff_q(&outq, p);
 628                else
 629                        /* no need to keep unmodified pairs */
 630                        pair_to_free = p;
 631
 632                if (pair_to_free)
 633                        diff_free_filepair(pair_to_free);
 634        }
 635        diff_debug_queue("done copying original", &outq);
 636
 637        free(q->queue);
 638        *q = outq;
 639        diff_debug_queue("done collapsing", q);
 640
 641        for (i = 0; i < rename_dst_nr; i++)
 642                free_filespec(rename_dst[i].two);
 643
 644        free(rename_dst);
 645        rename_dst = NULL;
 646        rename_dst_nr = rename_dst_alloc = 0;
 647        free(rename_src);
 648        rename_src = NULL;
 649        rename_src_nr = rename_src_alloc = 0;
 650        return;
 651}