diff.con commit [PATCH] Fix the way diffcore-rename records unremoved source. (15d061b)
   1/*
   2 * Copyright (C) 2005 Junio C Hamano
   3 */
   4#include <sys/types.h>
   5#include <sys/wait.h>
   6#include <signal.h>
   7#include "cache.h"
   8#include "diff.h"
   9#include "diffcore.h"
  10
  11static const char *diff_opts = "-pu";
  12static unsigned char null_sha1[20] = { 0, };
  13
  14static int reverse_diff;
  15
  16static const char *external_diff(void)
  17{
  18        static const char *external_diff_cmd = NULL;
  19        static int done_preparing = 0;
  20
  21        if (done_preparing)
  22                return external_diff_cmd;
  23
  24        /*
  25         * Default values above are meant to match the
  26         * Linux kernel development style.  Examples of
  27         * alternative styles you can specify via environment
  28         * variables are:
  29         *
  30         * GIT_DIFF_OPTS="-c";
  31         */
  32        if (gitenv("GIT_EXTERNAL_DIFF"))
  33                external_diff_cmd = gitenv("GIT_EXTERNAL_DIFF");
  34
  35        /* In case external diff fails... */
  36        diff_opts = gitenv("GIT_DIFF_OPTS") ? : diff_opts;
  37
  38        done_preparing = 1;
  39        return external_diff_cmd;
  40}
  41
  42/* Help to copy the thing properly quoted for the shell safety.
  43 * any single quote is replaced with '\'', and the caller is
  44 * expected to enclose the result within a single quote pair.
  45 *
  46 * E.g.
  47 *  original     sq_expand     result
  48 *  name     ==> name      ==> 'name'
  49 *  a b      ==> a b       ==> 'a b'
  50 *  a'b      ==> a'\''b    ==> 'a'\''b'
  51 */
  52static char *sq_expand(const char *src)
  53{
  54        static char *buf = NULL;
  55        int cnt, c;
  56        const char *cp;
  57        char *bp;
  58
  59        /* count bytes needed to store the quoted string. */
  60        for (cnt = 1, cp = src; *cp; cnt++, cp++)
  61                if (*cp == '\'')
  62                        cnt += 3;
  63
  64        buf = xmalloc(cnt);
  65        bp = buf;
  66        while ((c = *src++)) {
  67                if (c != '\'')
  68                        *bp++ = c;
  69                else {
  70                        bp = strcpy(bp, "'\\''");
  71                        bp += 4;
  72                }
  73        }
  74        *bp = 0;
  75        return buf;
  76}
  77
  78static struct diff_tempfile {
  79        const char *name; /* filename external diff should read from */
  80        char hex[41];
  81        char mode[10];
  82        char tmp_path[50];
  83} diff_temp[2];
  84
  85static void builtin_diff(const char *name_a,
  86                         const char *name_b,
  87                         struct diff_tempfile *temp,
  88                         const char *xfrm_msg)
  89{
  90        int i, next_at, cmd_size;
  91        const char *diff_cmd = "diff -L'%s%s' -L'%s%s'";
  92        const char *diff_arg  = "'%s' '%s'||:"; /* "||:" is to return 0 */
  93        const char *input_name_sq[2];
  94        const char *path0[2];
  95        const char *path1[2];
  96        const char *name_sq[2];
  97        char *cmd;
  98
  99        name_sq[0] = sq_expand(name_a);
 100        name_sq[1] = sq_expand(name_b);
 101
 102        /* diff_cmd and diff_arg have 6 %s in total which makes
 103         * the sum of these strings 12 bytes larger than required.
 104         * we use 2 spaces around diff-opts, and we need to count
 105         * terminating NUL, so we subtract 9 here.
 106         */
 107        cmd_size = (strlen(diff_cmd) + strlen(diff_opts) +
 108                        strlen(diff_arg) - 9);
 109        for (i = 0; i < 2; i++) {
 110                input_name_sq[i] = sq_expand(temp[i].name);
 111                if (!strcmp(temp[i].name, "/dev/null")) {
 112                        path0[i] = "/dev/null";
 113                        path1[i] = "";
 114                } else {
 115                        path0[i] = i ? "b/" : "a/";
 116                        path1[i] = name_sq[i];
 117                }
 118                cmd_size += (strlen(path0[i]) + strlen(path1[i]) +
 119                             strlen(input_name_sq[i]));
 120        }
 121
 122        cmd = xmalloc(cmd_size);
 123
 124        next_at = 0;
 125        next_at += snprintf(cmd+next_at, cmd_size-next_at,
 126                            diff_cmd,
 127                            path0[0], path1[0], path0[1], path1[1]);
 128        next_at += snprintf(cmd+next_at, cmd_size-next_at,
 129                            " %s ", diff_opts);
 130        next_at += snprintf(cmd+next_at, cmd_size-next_at,
 131                            diff_arg, input_name_sq[0], input_name_sq[1]);
 132
 133        printf("diff --git a/%s b/%s\n", name_a, name_b);
 134        if (!path1[0][0])
 135                printf("new file mode %s\n", temp[1].mode);
 136        else if (!path1[1][0])
 137                printf("deleted file mode %s\n", temp[0].mode);
 138        else {
 139                if (strcmp(temp[0].mode, temp[1].mode)) {
 140                        printf("old mode %s\n", temp[0].mode);
 141                        printf("new mode %s\n", temp[1].mode);
 142                }
 143                if (xfrm_msg && xfrm_msg[0])
 144                        puts(xfrm_msg);
 145
 146                if (strncmp(temp[0].mode, temp[1].mode, 3))
 147                        /* we do not run diff between different kind
 148                         * of objects.
 149                         */
 150                        exit(0);
 151        }
 152        fflush(NULL);
 153        execlp("/bin/sh","sh", "-c", cmd, NULL);
 154}
 155
 156struct diff_filespec *alloc_filespec(const char *path)
 157{
 158        int namelen = strlen(path);
 159        struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
 160        spec->path = (char *)(spec + 1);
 161        strcpy(spec->path, path);
 162        spec->should_free = spec->should_munmap = 0;
 163        spec->xfrm_flags = 0;
 164        spec->size = 0;
 165        spec->data = NULL;
 166        spec->mode = 0;
 167        memset(spec->sha1, 0, 20);
 168        return spec;
 169}
 170
 171void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
 172                   unsigned short mode)
 173{
 174        if (mode) {
 175                spec->mode = DIFF_FILE_CANON_MODE(mode);
 176                memcpy(spec->sha1, sha1, 20);
 177                spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
 178        }
 179}
 180
 181/*
 182 * Given a name and sha1 pair, if the dircache tells us the file in
 183 * the work tree has that object contents, return true, so that
 184 * prepare_temp_file() does not have to inflate and extract.
 185 */
 186static int work_tree_matches(const char *name, const unsigned char *sha1)
 187{
 188        struct cache_entry *ce;
 189        struct stat st;
 190        int pos, len;
 191
 192        /* We do not read the cache ourselves here, because the
 193         * benchmark with my previous version that always reads cache
 194         * shows that it makes things worse for diff-tree comparing
 195         * two linux-2.6 kernel trees in an already checked out work
 196         * tree.  This is because most diff-tree comparisons deal with
 197         * only a small number of files, while reading the cache is
 198         * expensive for a large project, and its cost outweighs the
 199         * savings we get by not inflating the object to a temporary
 200         * file.  Practically, this code only helps when we are used
 201         * by diff-cache --cached, which does read the cache before
 202         * calling us.
 203         */
 204        if (!active_cache)
 205                return 0;
 206
 207        len = strlen(name);
 208        pos = cache_name_pos(name, len);
 209        if (pos < 0)
 210                return 0;
 211        ce = active_cache[pos];
 212        if ((lstat(name, &st) < 0) ||
 213            !S_ISREG(st.st_mode) || /* careful! */
 214            ce_match_stat(ce, &st) ||
 215            memcmp(sha1, ce->sha1, 20))
 216                return 0;
 217        /* we return 1 only when we can stat, it is a regular file,
 218         * stat information matches, and sha1 recorded in the cache
 219         * matches.  I.e. we know the file in the work tree really is
 220         * the same as the <name, sha1> pair.
 221         */
 222        return 1;
 223}
 224
 225/*
 226 * While doing rename detection and pickaxe operation, we may need to
 227 * grab the data for the blob (or file) for our own in-core comparison.
 228 * diff_filespec has data and size fields for this purpose.
 229 */
 230int diff_populate_filespec(struct diff_filespec *s)
 231{
 232        int err = 0;
 233        if (!DIFF_FILE_VALID(s))
 234                die("internal error: asking to populate invalid file.");
 235        if (S_ISDIR(s->mode))
 236                return -1;
 237
 238        if (s->data)
 239                return err;
 240        if (!s->sha1_valid ||
 241            work_tree_matches(s->path, s->sha1)) {
 242                struct stat st;
 243                int fd;
 244                if (lstat(s->path, &st) < 0) {
 245                        if (errno == ENOENT) {
 246                        err_empty:
 247                                err = -1;
 248                        empty:
 249                                s->data = "";
 250                                s->size = 0;
 251                                return err;
 252                        }
 253                }
 254                s->size = st.st_size;
 255                if (!s->size)
 256                        goto empty;
 257                if (S_ISLNK(st.st_mode)) {
 258                        int ret;
 259                        s->data = xmalloc(s->size);
 260                        s->should_free = 1;
 261                        ret = readlink(s->path, s->data, s->size);
 262                        if (ret < 0) {
 263                                free(s->data);
 264                                goto err_empty;
 265                        }
 266                        return 0;
 267                }
 268                fd = open(s->path, O_RDONLY);
 269                if (fd < 0)
 270                        goto err_empty;
 271                s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
 272                s->should_munmap = 1;
 273                close(fd);
 274        }
 275        else {
 276                char type[20];
 277                s->data = read_sha1_file(s->sha1, type, &s->size);
 278                s->should_free = 1;
 279        }
 280        return 0;
 281}
 282
 283void diff_free_filespec_data(struct diff_filespec *s)
 284{
 285        if (s->should_free)
 286                free(s->data);
 287        else if (s->should_munmap)
 288                munmap(s->data, s->size);
 289        s->should_free = s->should_munmap = 0;
 290        s->data = NULL;
 291}
 292
 293static void prep_temp_blob(struct diff_tempfile *temp,
 294                           void *blob,
 295                           unsigned long size,
 296                           unsigned char *sha1,
 297                           int mode)
 298{
 299        int fd;
 300
 301        strcpy(temp->tmp_path, ".diff_XXXXXX");
 302        fd = mkstemp(temp->tmp_path);
 303        if (fd < 0)
 304                die("unable to create temp-file");
 305        if (write(fd, blob, size) != size)
 306                die("unable to write temp-file");
 307        close(fd);
 308        temp->name = temp->tmp_path;
 309        strcpy(temp->hex, sha1_to_hex(sha1));
 310        temp->hex[40] = 0;
 311        sprintf(temp->mode, "%06o", mode);
 312}
 313
 314static void prepare_temp_file(const char *name,
 315                              struct diff_tempfile *temp,
 316                              struct diff_filespec *one)
 317{
 318        if (!DIFF_FILE_VALID(one)) {
 319        not_a_valid_file:
 320                /* A '-' entry produces this for file-2, and
 321                 * a '+' entry produces this for file-1.
 322                 */
 323                temp->name = "/dev/null";
 324                strcpy(temp->hex, ".");
 325                strcpy(temp->mode, ".");
 326                return;
 327        }
 328
 329        if (!one->sha1_valid ||
 330            work_tree_matches(name, one->sha1)) {
 331                struct stat st;
 332                if (lstat(name, &st) < 0) {
 333                        if (errno == ENOENT)
 334                                goto not_a_valid_file;
 335                        die("stat(%s): %s", name, strerror(errno));
 336                }
 337                if (S_ISLNK(st.st_mode)) {
 338                        int ret;
 339                        char *buf, buf_[1024];
 340                        buf = ((sizeof(buf_) < st.st_size) ?
 341                               xmalloc(st.st_size) : buf_);
 342                        ret = readlink(name, buf, st.st_size);
 343                        if (ret < 0)
 344                                die("readlink(%s)", name);
 345                        prep_temp_blob(temp, buf, st.st_size,
 346                                       (one->sha1_valid ?
 347                                        one->sha1 : null_sha1),
 348                                       (one->sha1_valid ?
 349                                        one->mode : S_IFLNK));
 350                }
 351                else {
 352                        /* we can borrow from the file in the work tree */
 353                        temp->name = name;
 354                        if (!one->sha1_valid)
 355                                strcpy(temp->hex, sha1_to_hex(null_sha1));
 356                        else
 357                                strcpy(temp->hex, sha1_to_hex(one->sha1));
 358                        sprintf(temp->mode, "%06o",
 359                                S_IFREG |ce_permissions(st.st_mode));
 360                }
 361                return;
 362        }
 363        else {
 364                if (diff_populate_filespec(one))
 365                        die("cannot read data blob for %s", one->path);
 366                prep_temp_blob(temp, one->data, one->size,
 367                               one->sha1, one->mode);
 368        }
 369}
 370
 371static void remove_tempfile(void)
 372{
 373        int i;
 374
 375        for (i = 0; i < 2; i++)
 376                if (diff_temp[i].name == diff_temp[i].tmp_path) {
 377                        unlink(diff_temp[i].name);
 378                        diff_temp[i].name = NULL;
 379                }
 380}
 381
 382static void remove_tempfile_on_signal(int signo)
 383{
 384        remove_tempfile();
 385}
 386
 387/* An external diff command takes:
 388 *
 389 * diff-cmd name infile1 infile1-sha1 infile1-mode \
 390 *               infile2 infile2-sha1 infile2-mode [ rename-to ]
 391 *
 392 */
 393static void run_external_diff(const char *pgm,
 394                              const char *name,
 395                              const char *other,
 396                              struct diff_filespec *one,
 397                              struct diff_filespec *two,
 398                              const char *xfrm_msg)
 399{
 400        struct diff_tempfile *temp = diff_temp;
 401        pid_t pid;
 402        int status;
 403        static int atexit_asked = 0;
 404
 405        if (one && two) {
 406                prepare_temp_file(name, &temp[0], one);
 407                prepare_temp_file(other ? : name, &temp[1], two);
 408                if (! atexit_asked &&
 409                    (temp[0].name == temp[0].tmp_path ||
 410                     temp[1].name == temp[1].tmp_path)) {
 411                        atexit_asked = 1;
 412                        atexit(remove_tempfile);
 413                }
 414                signal(SIGINT, remove_tempfile_on_signal);
 415        }
 416
 417        fflush(NULL);
 418        pid = fork();
 419        if (pid < 0)
 420                die("unable to fork");
 421        if (!pid) {
 422                if (pgm) {
 423                        if (one && two) {
 424                                const char *exec_arg[10];
 425                                const char **arg = &exec_arg[0];
 426                                *arg++ = pgm;
 427                                *arg++ = name;
 428                                *arg++ = temp[0].name;
 429                                *arg++ = temp[0].hex;
 430                                *arg++ = temp[0].mode;
 431                                *arg++ = temp[1].name;
 432                                *arg++ = temp[1].hex;
 433                                *arg++ = temp[1].mode;
 434                                if (other) {
 435                                        *arg++ = other;
 436                                        *arg++ = xfrm_msg;
 437                                }
 438                                *arg = NULL;
 439                                execvp(pgm, (char *const*) exec_arg);
 440                        }
 441                        else
 442                                execlp(pgm, pgm, name, NULL);
 443                }
 444                /*
 445                 * otherwise we use the built-in one.
 446                 */
 447                if (one && two)
 448                        builtin_diff(name, other ? : name, temp, xfrm_msg);
 449                else
 450                        printf("* Unmerged path %s\n", name);
 451                exit(0);
 452        }
 453        if (waitpid(pid, &status, 0) < 0 ||
 454            !WIFEXITED(status) || WEXITSTATUS(status)) {
 455                /* Earlier we did not check the exit status because
 456                 * diff exits non-zero if files are different, and
 457                 * we are not interested in knowing that.  It was a
 458                 * mistake which made it harder to quit a diff-*
 459                 * session that uses the git-apply-patch-script as
 460                 * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
 461                 * should also exit non-zero only when it wants to
 462                 * abort the entire diff-* session.
 463                 */
 464                remove_tempfile();
 465                fprintf(stderr, "external diff died, stopping at %s.\n", name);
 466                exit(1);
 467        }
 468        remove_tempfile();
 469}
 470
 471static void run_diff(const char *name,
 472                     const char *other,
 473                     struct diff_filespec *one,
 474                     struct diff_filespec *two,
 475                     const char *xfrm_msg)
 476{
 477        const char *pgm = external_diff();
 478        if (!pgm &&
 479            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
 480            (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
 481                /* a filepair that changes between file and symlink
 482                 * needs to be split into deletion and creation.
 483                 */
 484                struct diff_filespec *null = alloc_filespec(two->path);
 485                run_external_diff(NULL, name, other, one, null, xfrm_msg);
 486                free(null);
 487                null = alloc_filespec(one->path);
 488                run_external_diff(NULL, name, other, null, two, xfrm_msg);
 489                free(null);
 490        }
 491        else
 492                run_external_diff(pgm, name, other, one, two, xfrm_msg);
 493}
 494
 495void diff_setup(int flags)
 496{
 497        if (flags & DIFF_SETUP_REVERSE)
 498                reverse_diff = 1;
 499}
 500
 501struct diff_queue_struct diff_queued_diff;
 502
 503void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
 504{
 505        if (queue->alloc <= queue->nr) {
 506                queue->alloc = alloc_nr(queue->alloc);
 507                queue->queue = xrealloc(queue->queue,
 508                                        sizeof(dp) * queue->alloc);
 509        }
 510        queue->queue[queue->nr++] = dp;
 511}
 512
 513struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
 514                                 struct diff_filespec *one,
 515                                 struct diff_filespec *two)
 516{
 517        struct diff_filepair *dp = xmalloc(sizeof(*dp));
 518        dp->one = one;
 519        dp->two = two;
 520        dp->score = 0;
 521        dp->source_stays = 0;
 522        diff_q(queue, dp);
 523        return dp;
 524}
 525
 526void diff_free_filepair(struct diff_filepair *p)
 527{
 528        diff_free_filespec_data(p->one);
 529        diff_free_filespec_data(p->two);
 530        free(p);
 531}
 532
 533static void diff_flush_raw(struct diff_filepair *p,
 534                           int line_termination,
 535                           int inter_name_termination)
 536{
 537        int two_paths;
 538        char status[10];
 539
 540        if (line_termination) {
 541                const char *err = "path %s cannot be expressed without -z";
 542                if (strchr(p->one->path, line_termination) ||
 543                    strchr(p->one->path, inter_name_termination))
 544                        die(err, p->one->path);
 545                if (strchr(p->two->path, line_termination) ||
 546                    strchr(p->two->path, inter_name_termination))
 547                        die(err, p->two->path);
 548        }
 549
 550        switch (p->status) {
 551        case 'C': case 'R':
 552                two_paths = 1;
 553                sprintf(status, "%c%03d", p->status,
 554                        (int)(0.5 + p->score * 100.0/MAX_SCORE));
 555                break;
 556        default:
 557                two_paths = 0;
 558                status[0] = p->status;
 559                status[1] = 0;
 560                break;
 561        }
 562        printf(":%06o %06o %s ",
 563               p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));
 564        printf("%s %s%c%s",
 565               sha1_to_hex(p->two->sha1),
 566               status,
 567               inter_name_termination,
 568               p->one->path);
 569        if (two_paths)
 570                printf("%c%s", inter_name_termination, p->two->path);
 571        putchar(line_termination);
 572}
 573
 574int diff_unmodified_pair(struct diff_filepair *p)
 575{
 576        /* This function is written stricter than necessary to support
 577         * the currently implemented transformers, but the idea is to
 578         * let transformers to produce diff_filepairs any way they want,
 579         * and filter and clean them up here before producing the output.
 580         */
 581        struct diff_filespec *one, *two;
 582
 583        if (DIFF_PAIR_UNMERGED(p))
 584                return 0; /* unmerged is interesting */
 585
 586        one = p->one;
 587        two = p->two;
 588
 589        /* deletion, addition, mode or type change
 590         * and rename are all interesting.
 591         */
 592        if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
 593            DIFF_PAIR_MODE_CHANGED(p) ||
 594            strcmp(one->path, two->path))
 595                return 0;
 596
 597        /* both are valid and point at the same path.  that is, we are
 598         * dealing with a change.
 599         */
 600        if (one->sha1_valid && two->sha1_valid &&
 601            !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
 602                return 1; /* no change */
 603        if (!one->sha1_valid && !two->sha1_valid)
 604                return 1; /* both look at the same file on the filesystem. */
 605        return 0;
 606}
 607
 608static void diff_flush_patch(struct diff_filepair *p)
 609{
 610        const char *name, *other;
 611        char msg_[PATH_MAX*2+200], *msg;
 612
 613        if (diff_unmodified_pair(p))
 614                return;
 615
 616        name = p->one->path;
 617        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 618        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
 619            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
 620                return; /* no tree diffs in patch format */ 
 621
 622        switch (p->status) {
 623        case 'C':
 624                sprintf(msg_,
 625                        "similarity index %d%%\n"
 626                        "copy from %s\n"
 627                        "copy to %s",
 628                        (int)(0.5 + p->score * 100.0/MAX_SCORE),
 629                        p->one->path, p->two->path);
 630                msg = msg_;
 631                break;
 632        case 'R':
 633                sprintf(msg_,
 634                        "similarity index %d%%\n"
 635                        "rename old %s\n"
 636                        "rename new %s",
 637                        (int)(0.5 + p->score * 100.0/MAX_SCORE),
 638                        p->one->path, p->two->path);
 639                msg = msg_;
 640                break;
 641        default:
 642                msg = NULL;
 643        }
 644
 645        if (DIFF_PAIR_UNMERGED(p))
 646                run_diff(name, NULL, NULL, NULL, NULL);
 647        else
 648                run_diff(name, other, p->one, p->two, msg);
 649}
 650
 651int diff_queue_is_empty(void)
 652{
 653        struct diff_queue_struct *q = &diff_queued_diff;
 654        int i;
 655        for (i = 0; i < q->nr; i++)
 656                if (!diff_unmodified_pair(q->queue[i]))
 657                        return 0;
 658        return 1;
 659}
 660
 661#if DIFF_DEBUG
 662void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
 663{
 664        fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
 665                x, one ? : "",
 666                s->path,
 667                DIFF_FILE_VALID(s) ? "valid" : "invalid",
 668                s->mode,
 669                s->sha1_valid ? sha1_to_hex(s->sha1) : "");
 670        fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
 671                x, one ? : "",
 672                s->size, s->xfrm_flags);
 673}
 674
 675void diff_debug_filepair(const struct diff_filepair *p, int i)
 676{
 677        diff_debug_filespec(p->one, i, "one");
 678        diff_debug_filespec(p->two, i, "two");
 679        fprintf(stderr, "score %d, status %c source_stays %d\n",
 680                p->score, p->status ? : '?', p->source_stays);
 681}
 682
 683void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
 684{
 685        int i;
 686        if (msg)
 687                fprintf(stderr, "%s\n", msg);
 688        fprintf(stderr, "q->nr = %d\n", q->nr);
 689        for (i = 0; i < q->nr; i++) {
 690                struct diff_filepair *p = q->queue[i];
 691                diff_debug_filepair(p, i);
 692        }
 693}
 694#endif
 695
 696static void diff_resolve_rename_copy(void)
 697{
 698        int i, j;
 699        struct diff_filepair *p, *pp;
 700        struct diff_queue_struct *q = &diff_queued_diff;
 701
 702        diff_debug_queue("resolve-rename-copy", q);
 703
 704        for (i = 0; i < q->nr; i++) {
 705                p = q->queue[i];
 706                p->status = 0; /* undecided */
 707                if (DIFF_PAIR_UNMERGED(p))
 708                        p->status = 'U';
 709                else if (!DIFF_FILE_VALID(p->one))
 710                        p->status = 'N';
 711                else if (!DIFF_FILE_VALID(p->two)) {
 712                        /* Deleted entry may have been picked up by
 713                         * another rename-copy entry.  So we scan the
 714                         * queue and if we find one that uses us as the
 715                         * source we do not say delete for this entry.
 716                         */
 717                        for (j = 0; j < q->nr; j++) {
 718                                pp = q->queue[j];
 719                                if (!strcmp(p->one->path, pp->one->path) &&
 720                                    pp->score) {
 721                                        /* rename/copy are always valid
 722                                         * so we do not say DIFF_FILE_VALID()
 723                                         * on pp->one and pp->two.
 724                                         */
 725                                        p->status = 'X';
 726                                        break;
 727                                }
 728                        }
 729                        if (!p->status)
 730                                p->status = 'D';
 731                }
 732                else if (DIFF_PAIR_TYPE_CHANGED(p))
 733                        p->status = 'T';
 734
 735                /* from this point on, we are dealing with a pair
 736                 * whose both sides are valid and of the same type, i.e.
 737                 * either in-place edit or rename/copy edit.
 738                 */
 739                else if (p->score) {
 740                        if (p->source_stays) {
 741                                p->status = 'C';
 742                                continue;
 743                        }
 744                        /* See if there is some other filepair that
 745                         * copies from the same source as us.  If so
 746                         * we are a copy.  Otherwise we are a rename.
 747                         */
 748                        for (j = i + 1; j < q->nr; j++) {
 749                                pp = q->queue[j];
 750                                if (strcmp(pp->one->path, p->one->path))
 751                                        continue; /* not us */
 752                                if (!pp->score)
 753                                        continue; /* not a rename/copy */
 754                                /* pp is a rename/copy from the same source */
 755                                p->status = 'C';
 756                                break;
 757                        }
 758                        if (!p->status)
 759                                p->status = 'R';
 760                }
 761                else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
 762                         p->one->mode != p->two->mode)
 763                        p->status = 'M';
 764                else
 765                        /* this is a "no-change" entry.
 766                         * should not happen anymore.
 767                         * p->status = 'X';
 768                         */
 769                        die("internal error in diffcore: unmodified entry remains");
 770        }
 771        diff_debug_queue("resolve-rename-copy done", q);
 772}
 773
 774void diff_flush(int diff_output_style, int resolve_rename_copy)
 775{
 776        struct diff_queue_struct *q = &diff_queued_diff;
 777        int i;
 778        int line_termination = '\n';
 779        int inter_name_termination = '\t';
 780
 781        if (diff_output_style == DIFF_FORMAT_MACHINE)
 782                line_termination = inter_name_termination = 0;
 783        if (resolve_rename_copy)
 784                diff_resolve_rename_copy();
 785
 786        for (i = 0; i < q->nr; i++) {
 787                struct diff_filepair *p = q->queue[i];
 788                if ((diff_output_style == DIFF_FORMAT_NO_OUTPUT) ||
 789                    (p->status == 'X'))
 790                        continue;
 791                if (p->status == 0)
 792                        die("internal error in diff-resolve-rename-copy");
 793                switch (diff_output_style) {
 794                case DIFF_FORMAT_PATCH:
 795                        diff_flush_patch(p);
 796                        break;
 797                case DIFF_FORMAT_HUMAN:
 798                case DIFF_FORMAT_MACHINE:
 799                        diff_flush_raw(p, line_termination,
 800                                       inter_name_termination);
 801                        break;
 802                }
 803        }
 804        for (i = 0; i < q->nr; i++)
 805                diff_free_filepair(q->queue[i]);
 806        free(q->queue);
 807        q->queue = NULL;
 808        q->nr = q->alloc = 0;
 809}
 810
 811void diff_addremove(int addremove, unsigned mode,
 812                    const unsigned char *sha1,
 813                    const char *base, const char *path)
 814{
 815        char concatpath[PATH_MAX];
 816        struct diff_filespec *one, *two;
 817
 818        /* This may look odd, but it is a preparation for
 819         * feeding "there are unchanged files which should
 820         * not produce diffs, but when you are doing copy
 821         * detection you would need them, so here they are"
 822         * entries to the diff-core.  They will be prefixed
 823         * with something like '=' or '*' (I haven't decided
 824         * which but should not make any difference).
 825         * Feeding the same new and old to diff_change() 
 826         * also has the same effect.
 827         * Before the final output happens, they are pruned after
 828         * merged into rename/copy pairs as appropriate.
 829         */
 830        if (reverse_diff)
 831                addremove = (addremove == '+' ? '-' :
 832                             addremove == '-' ? '+' : addremove);
 833
 834        if (!path) path = "";
 835        sprintf(concatpath, "%s%s", base, path);
 836        one = alloc_filespec(concatpath);
 837        two = alloc_filespec(concatpath);
 838
 839        if (addremove != '+')
 840                fill_filespec(one, sha1, mode);
 841        if (addremove != '-')
 842                fill_filespec(two, sha1, mode);
 843
 844        diff_queue(&diff_queued_diff, one, two);
 845}
 846
 847void diff_helper_input(unsigned old_mode,
 848                       unsigned new_mode,
 849                       const unsigned char *old_sha1,
 850                       const unsigned char *new_sha1,
 851                       const char *old_path,
 852                       int status,
 853                       int score,
 854                       const char *new_path)
 855{
 856        struct diff_filespec *one, *two;
 857        struct diff_filepair *dp;
 858
 859        one = alloc_filespec(old_path);
 860        two = alloc_filespec(new_path);
 861        if (old_mode)
 862                fill_filespec(one, old_sha1, old_mode);
 863        if (new_mode)
 864                fill_filespec(two, new_sha1, new_mode);
 865        dp = diff_queue(&diff_queued_diff, one, two);
 866        dp->score = score * MAX_SCORE / 100;
 867        dp->status = status;
 868}
 869
 870void diff_change(unsigned old_mode, unsigned new_mode,
 871                 const unsigned char *old_sha1,
 872                 const unsigned char *new_sha1,
 873                 const char *base, const char *path) 
 874{
 875        char concatpath[PATH_MAX];
 876        struct diff_filespec *one, *two;
 877
 878        if (reverse_diff) {
 879                unsigned tmp;
 880                const unsigned char *tmp_c;
 881                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
 882                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
 883        }
 884        if (!path) path = "";
 885        sprintf(concatpath, "%s%s", base, path);
 886        one = alloc_filespec(concatpath);
 887        two = alloc_filespec(concatpath);
 888        fill_filespec(one, old_sha1, old_mode);
 889        fill_filespec(two, new_sha1, new_mode);
 890
 891        diff_queue(&diff_queued_diff, one, two);
 892}
 893
 894void diff_unmerge(const char *path)
 895{
 896        struct diff_filespec *one, *two;
 897        one = alloc_filespec(path);
 898        two = alloc_filespec(path);
 899        diff_queue(&diff_queued_diff, one, two);
 900}