diff.con commit [PATCH] adjust git-deltafy-script to the new diff-tree output format (587e494)
   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                        fputs(xfrm_msg, stdout);
 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) { /* just playing defensive */
 175                spec->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 *name,
 394                              const char *other,
 395                              struct diff_filespec *one,
 396                              struct diff_filespec *two,
 397                              const char *xfrm_msg)
 398{
 399        struct diff_tempfile *temp = diff_temp;
 400        pid_t pid;
 401        int status;
 402        static int atexit_asked = 0;
 403
 404        if (one && two) {
 405                prepare_temp_file(name, &temp[0], one);
 406                prepare_temp_file(other ? : name, &temp[1], two);
 407                if (! atexit_asked &&
 408                    (temp[0].name == temp[0].tmp_path ||
 409                     temp[1].name == temp[1].tmp_path)) {
 410                        atexit_asked = 1;
 411                        atexit(remove_tempfile);
 412                }
 413                signal(SIGINT, remove_tempfile_on_signal);
 414        }
 415
 416        fflush(NULL);
 417        pid = fork();
 418        if (pid < 0)
 419                die("unable to fork");
 420        if (!pid) {
 421                const char *pgm = external_diff();
 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
 471void diff_setup(int reverse_diff_)
 472{
 473        reverse_diff = reverse_diff_;
 474}
 475
 476struct diff_queue_struct diff_queued_diff;
 477
 478void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
 479{
 480        if (queue->alloc <= queue->nr) {
 481                queue->alloc = alloc_nr(queue->alloc);
 482                queue->queue = xrealloc(queue->queue,
 483                                        sizeof(dp) * queue->alloc);
 484        }
 485        queue->queue[queue->nr++] = dp;
 486}
 487
 488struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
 489                                 struct diff_filespec *one,
 490                                 struct diff_filespec *two)
 491{
 492        struct diff_filepair *dp = xmalloc(sizeof(*dp));
 493        dp->one = one;
 494        dp->two = two;
 495        dp->score = 0;
 496        dp->orig_order = queue->nr;
 497        dp->rename_rank = 0;
 498        diff_q(queue, dp);
 499        return dp;
 500}
 501
 502static void diff_flush_raw(struct diff_filepair *p,
 503                           int line_termination,
 504                           int inter_name_termination)
 505{
 506        int two_paths;
 507        char status[10];
 508        switch (p->status) {
 509        case 'C': case 'R':
 510                two_paths = 1;
 511                sprintf(status, "%c%1d", p->status, p->score);
 512                break;
 513        default:
 514                two_paths = 0;
 515                status[0] = p->status;
 516                status[1] = 0;
 517                break;
 518        }
 519        printf(":%06o %06o %s ",
 520               p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));
 521        printf("%s %s%c%s",
 522               sha1_to_hex(p->two->sha1),
 523               status,
 524               inter_name_termination,
 525               p->one->path);
 526        if (two_paths)
 527                printf("%c%s", inter_name_termination, p->two->path);
 528        putchar(line_termination);
 529}
 530
 531int diff_unmodified_pair(struct diff_filepair *p)
 532{
 533        /* This function is written stricter than necessary to support
 534         * the currently implemented transformers, but the idea is to
 535         * let transformers to produce diff_filepairs any way they want,
 536         * and filter and clean them up here before producing the output.
 537         */
 538        struct diff_filespec *one, *two;
 539
 540        if (DIFF_PAIR_UNMERGED(p))
 541                return 0; /* unmerged is interesting */
 542
 543        one = p->one;
 544        two = p->two;
 545
 546        /* deletion, addition, mode change and renames are all interesting. */
 547        if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
 548            (one->mode != two->mode) ||
 549            strcmp(one->path, two->path))
 550                return 0;
 551
 552        /* both are valid and point at the same path.  that is, we are
 553         * dealing with a change.
 554         */
 555        if (one->sha1_valid && two->sha1_valid &&
 556            !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
 557                return 1; /* no change */
 558        if (!one->sha1_valid && !two->sha1_valid)
 559                return 1; /* both look at the same file on the filesystem. */
 560        return 0;
 561}
 562
 563static void diff_flush_patch(struct diff_filepair *p)
 564{
 565        const char *name, *other;
 566        char msg_[PATH_MAX*2+200], *msg;
 567
 568        if (diff_unmodified_pair(p))
 569                return;
 570
 571        name = p->one->path;
 572        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 573        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
 574            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
 575                return; /* no tree diffs in patch format */ 
 576
 577        switch (p->status) {
 578        case 'C':
 579                sprintf(msg_,
 580                        "similarity index %d%%\n"
 581                        "copy from %s\n"
 582                        "copy to %s\n",
 583                        (int)(0.5 + p->score * 100.0/MAX_SCORE),
 584                        p->one->path, p->two->path);
 585                msg = msg_;
 586                break;
 587        case 'R':
 588                sprintf(msg_,
 589                        "similarity index %d%%\n"
 590                        "rename old %s\n"
 591                        "rename new %s\n",
 592                        (int)(0.5 + p->score * 100.0/MAX_SCORE),
 593                        p->one->path, p->two->path);
 594                msg = msg_;
 595                break;
 596        default:
 597                msg = NULL;
 598        }
 599
 600        if (DIFF_PAIR_UNMERGED(p))
 601                run_external_diff(name, NULL, NULL, NULL, NULL);
 602        else
 603                run_external_diff(name, other, p->one, p->two, msg);
 604}
 605
 606int diff_needs_to_stay(struct diff_queue_struct *q, int i,
 607                       struct diff_filespec *it)
 608{
 609        /* If it will be used in later entry (either stay or used
 610         * as the source of rename/copy), we need to copy, not rename.
 611         */
 612        while (i < q->nr) {
 613                struct diff_filepair *p = q->queue[i++];
 614                if (!DIFF_FILE_VALID(p->two))
 615                        continue; /* removed is fine */
 616                if (strcmp(p->one->path, it->path))
 617                        continue; /* not relevant */
 618
 619                /* p has its src set to *it and it is not a delete;
 620                 * it will be used for in-place change, rename/copy,
 621                 * or just stays there.  We cannot rename it out.
 622                 */
 623                return 1;
 624        }
 625        return 0;
 626}
 627
 628int diff_queue_is_empty(void)
 629{
 630        struct diff_queue_struct *q = &diff_queued_diff;
 631        return q->nr == 0;
 632}
 633
 634static void diff_resolve_rename_copy(void)
 635{
 636        int i;
 637        struct diff_queue_struct *q = &diff_queued_diff;
 638        for (i = 0; i < q->nr; i++) {
 639                struct diff_filepair *p = q->queue[i];
 640                p->status = 0;
 641                if (DIFF_PAIR_UNMERGED(p))
 642                        p->status = 'U';
 643                else if (!DIFF_FILE_VALID((p)->one))
 644                        p->status = 'N';
 645                else if (!DIFF_FILE_VALID((p)->two)) {
 646                        /* maybe earlier one said 'R', meaning
 647                         * it will take it, in which case we do
 648                         * not need to keep 'D'.
 649                         */
 650                        int j;
 651                        for (j = 0; j < i; j++) {
 652                                struct diff_filepair *pp = q->queue[j];
 653                                if (pp->status == 'R' &&
 654                                    !strcmp(pp->one->path, p->one->path))
 655                                        break;
 656                        }
 657                        if (j < i)
 658                                continue;
 659                        p->status = 'D';
 660                }
 661                else if (strcmp(p->one->path, p->two->path)) {
 662                        /* This is rename or copy.  Which one is it? */
 663                        if (diff_needs_to_stay(q, i+1, p->one))
 664                                p->status = 'C';
 665                        else
 666                                p->status = 'R';
 667                }
 668                else if (memcmp(p->one->sha1, p->two->sha1, 20))
 669                        p->status = 'M';
 670                else {
 671                        /* we do not need this one */
 672                        p->status = 0;
 673                }
 674        }
 675}
 676
 677void diff_flush(int diff_output_style, int resolve_rename_copy)
 678{
 679        struct diff_queue_struct *q = &diff_queued_diff;
 680        int i;
 681        int line_termination = '\n';
 682        int inter_name_termination = '\t';
 683
 684        if (diff_output_style == DIFF_FORMAT_MACHINE)
 685                line_termination = inter_name_termination = 0;
 686        if (resolve_rename_copy)
 687                diff_resolve_rename_copy();
 688
 689        for (i = 0; i < q->nr; i++) {
 690                struct diff_filepair *p = q->queue[i];
 691                if (p->status == 0)
 692                        continue;
 693                switch (diff_output_style) {
 694                case DIFF_FORMAT_PATCH:
 695                        diff_flush_patch(p);
 696                        break;
 697                case DIFF_FORMAT_HUMAN:
 698                case DIFF_FORMAT_MACHINE:
 699                        diff_flush_raw(p, line_termination,
 700                                       inter_name_termination);
 701                        break;
 702                }
 703        }
 704        for (i = 0; i < q->nr; i++) {
 705                struct diff_filepair *p = q->queue[i];
 706                diff_free_filespec_data(p->one);
 707                diff_free_filespec_data(p->two);
 708                free(p);
 709        }
 710        free(q->queue);
 711        q->queue = NULL;
 712        q->nr = q->alloc = 0;
 713}
 714
 715void diff_addremove(int addremove, unsigned mode,
 716                    const unsigned char *sha1,
 717                    const char *base, const char *path)
 718{
 719        char concatpath[PATH_MAX];
 720        struct diff_filespec *one, *two;
 721
 722        /* This may look odd, but it is a preparation for
 723         * feeding "there are unchanged files which should
 724         * not produce diffs, but when you are doing copy
 725         * detection you would need them, so here they are"
 726         * entries to the diff-core.  They will be prefixed
 727         * with something like '=' or '*' (I haven't decided
 728         * which but should not make any difference).
 729         * Feeding the same new and old to diff_change() 
 730         * also has the same effect.
 731         * Before the final output happens, they are pruned after
 732         * merged into rename/copy pairs as appropriate.
 733         */
 734        if (reverse_diff)
 735                addremove = (addremove == '+' ? '-' :
 736                             addremove == '-' ? '+' : addremove);
 737
 738        if (!path) path = "";
 739        sprintf(concatpath, "%s%s", base, path);
 740        one = alloc_filespec(concatpath);
 741        two = alloc_filespec(concatpath);
 742
 743        if (addremove != '+')
 744                fill_filespec(one, sha1, mode);
 745        if (addremove != '-')
 746                fill_filespec(two, sha1, mode);
 747
 748        diff_queue(&diff_queued_diff, one, two);
 749}
 750
 751void diff_helper_input(unsigned old_mode,
 752                       unsigned new_mode,
 753                       const unsigned char *old_sha1,
 754                       const unsigned char *new_sha1,
 755                       const char *old_path,
 756                       int status,
 757                       int score,
 758                       const char *new_path)
 759{
 760        struct diff_filespec *one, *two;
 761        struct diff_filepair *dp;
 762
 763        one = alloc_filespec(old_path);
 764        two = alloc_filespec(new_path);
 765        if (old_mode)
 766                fill_filespec(one, old_sha1, old_mode);
 767        if (new_mode)
 768                fill_filespec(two, new_sha1, new_mode);
 769        dp = diff_queue(&diff_queued_diff, one, two);
 770        dp->score = score;
 771        dp->status = status;
 772}
 773
 774void diff_change(unsigned old_mode, unsigned new_mode,
 775                 const unsigned char *old_sha1,
 776                 const unsigned char *new_sha1,
 777                 const char *base, const char *path) 
 778{
 779        char concatpath[PATH_MAX];
 780        struct diff_filespec *one, *two;
 781
 782        if (reverse_diff) {
 783                unsigned tmp;
 784                const unsigned char *tmp_c;
 785                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
 786                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
 787        }
 788        if (!path) path = "";
 789        sprintf(concatpath, "%s%s", base, path);
 790        one = alloc_filespec(concatpath);
 791        two = alloc_filespec(concatpath);
 792        fill_filespec(one, old_sha1, old_mode);
 793        fill_filespec(two, new_sha1, new_mode);
 794
 795        diff_queue(&diff_queued_diff, one, two);
 796}
 797
 798void diff_unmerge(const char *path)
 799{
 800        struct diff_filespec *one, *two;
 801        one = alloc_filespec(path);
 802        two = alloc_filespec(path);
 803        diff_queue(&diff_queued_diff, one, two);
 804}