combine-diff.con commit expose a helper function peel_to_type(). (8177631)
   1#include "cache.h"
   2#include "commit.h"
   3#include "blob.h"
   4#include "diff.h"
   5#include "diffcore.h"
   6#include "quote.h"
   7#include "xdiff-interface.h"
   8#include "log-tree.h"
   9
  10static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
  11{
  12        struct diff_queue_struct *q = &diff_queued_diff;
  13        struct combine_diff_path *p;
  14        int i;
  15
  16        if (!n) {
  17                struct combine_diff_path *list = NULL, **tail = &list;
  18                for (i = 0; i < q->nr; i++) {
  19                        int len;
  20                        const char *path;
  21                        if (diff_unmodified_pair(q->queue[i]))
  22                                continue;
  23                        path = q->queue[i]->two->path;
  24                        len = strlen(path);
  25                        p = xmalloc(combine_diff_path_size(num_parent, len));
  26                        p->path = (char*) &(p->parent[num_parent]);
  27                        memcpy(p->path, path, len);
  28                        p->path[len] = 0;
  29                        p->len = len;
  30                        p->next = NULL;
  31                        memset(p->parent, 0,
  32                               sizeof(p->parent[0]) * num_parent);
  33
  34                        hashcpy(p->sha1, q->queue[i]->two->sha1);
  35                        p->mode = q->queue[i]->two->mode;
  36                        hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
  37                        p->parent[n].mode = q->queue[i]->one->mode;
  38                        p->parent[n].status = q->queue[i]->status;
  39                        *tail = p;
  40                        tail = &p->next;
  41                }
  42                return list;
  43        }
  44
  45        for (p = curr; p; p = p->next) {
  46                int found = 0;
  47                if (!p->len)
  48                        continue;
  49                for (i = 0; i < q->nr; i++) {
  50                        const char *path;
  51                        int len;
  52
  53                        if (diff_unmodified_pair(q->queue[i]))
  54                                continue;
  55                        path = q->queue[i]->two->path;
  56                        len = strlen(path);
  57                        if (len == p->len && !memcmp(path, p->path, len)) {
  58                                found = 1;
  59                                hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
  60                                p->parent[n].mode = q->queue[i]->one->mode;
  61                                p->parent[n].status = q->queue[i]->status;
  62                                break;
  63                        }
  64                }
  65                if (!found)
  66                        p->len = 0;
  67        }
  68        return curr;
  69}
  70
  71/* Lines lost from parent */
  72struct lline {
  73        struct lline *next;
  74        int len;
  75        unsigned long parent_map;
  76        char line[FLEX_ARRAY];
  77};
  78
  79/* Lines surviving in the merge result */
  80struct sline {
  81        struct lline *lost_head, **lost_tail;
  82        char *bol;
  83        int len;
  84        /* bit 0 up to (N-1) are on if the parent has this line (i.e.
  85         * we did not change it).
  86         * bit N is used for "interesting" lines, including context.
  87         */
  88        unsigned long flag;
  89        unsigned long *p_lno;
  90};
  91
  92static char *grab_blob(const unsigned char *sha1, unsigned long *size)
  93{
  94        char *blob;
  95        enum object_type type;
  96        if (is_null_sha1(sha1)) {
  97                /* deleted blob */
  98                *size = 0;
  99                return xcalloc(1, 1);
 100        }
 101        blob = read_sha1_file(sha1, &type, size);
 102        if (type != OBJ_BLOB)
 103                die("object '%s' is not a blob!", sha1_to_hex(sha1));
 104        return blob;
 105}
 106
 107static void append_lost(struct sline *sline, int n, const char *line, int len)
 108{
 109        struct lline *lline;
 110        unsigned long this_mask = (1UL<<n);
 111        if (line[len-1] == '\n')
 112                len--;
 113
 114        /* Check to see if we can squash things */
 115        if (sline->lost_head) {
 116                struct lline *last_one = NULL;
 117                /* We cannot squash it with earlier one */
 118                for (lline = sline->lost_head;
 119                     lline;
 120                     lline = lline->next)
 121                        if (lline->parent_map & this_mask)
 122                                last_one = lline;
 123                lline = last_one ? last_one->next : sline->lost_head;
 124                while (lline) {
 125                        if (lline->len == len &&
 126                            !memcmp(lline->line, line, len)) {
 127                                lline->parent_map |= this_mask;
 128                                return;
 129                        }
 130                        lline = lline->next;
 131                }
 132        }
 133
 134        lline = xmalloc(sizeof(*lline) + len + 1);
 135        lline->len = len;
 136        lline->next = NULL;
 137        lline->parent_map = this_mask;
 138        memcpy(lline->line, line, len);
 139        lline->line[len] = 0;
 140        *sline->lost_tail = lline;
 141        sline->lost_tail = &lline->next;
 142}
 143
 144struct combine_diff_state {
 145        struct xdiff_emit_state xm;
 146
 147        unsigned int lno;
 148        int ob, on, nb, nn;
 149        unsigned long nmask;
 150        int num_parent;
 151        int n;
 152        struct sline *sline;
 153        struct sline *lost_bucket;
 154};
 155
 156static void consume_line(void *state_, char *line, unsigned long len)
 157{
 158        struct combine_diff_state *state = state_;
 159        if (5 < len && !memcmp("@@ -", line, 4)) {
 160                if (parse_hunk_header(line, len,
 161                                      &state->ob, &state->on,
 162                                      &state->nb, &state->nn))
 163                        return;
 164                state->lno = state->nb;
 165                if (!state->nb)
 166                        /* @@ -1,2 +0,0 @@ to remove the
 167                         * first two lines...
 168                         */
 169                        state->nb = 1;
 170                if (state->nn == 0)
 171                        /* @@ -X,Y +N,0 @@ removed Y lines
 172                         * that would have come *after* line N
 173                         * in the result.  Our lost buckets hang
 174                         * to the line after the removed lines,
 175                         */
 176                        state->lost_bucket = &state->sline[state->nb];
 177                else
 178                        state->lost_bucket = &state->sline[state->nb-1];
 179                if (!state->sline[state->nb-1].p_lno)
 180                        state->sline[state->nb-1].p_lno =
 181                                xcalloc(state->num_parent,
 182                                        sizeof(unsigned long));
 183                state->sline[state->nb-1].p_lno[state->n] = state->ob;
 184                return;
 185        }
 186        if (!state->lost_bucket)
 187                return; /* not in any hunk yet */
 188        switch (line[0]) {
 189        case '-':
 190                append_lost(state->lost_bucket, state->n, line+1, len-1);
 191                break;
 192        case '+':
 193                state->sline[state->lno-1].flag |= state->nmask;
 194                state->lno++;
 195                break;
 196        }
 197}
 198
 199static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
 200                         struct sline *sline, unsigned int cnt, int n,
 201                         int num_parent)
 202{
 203        unsigned int p_lno, lno;
 204        unsigned long nmask = (1UL << n);
 205        xpparam_t xpp;
 206        xdemitconf_t xecfg;
 207        mmfile_t parent_file;
 208        xdemitcb_t ecb;
 209        struct combine_diff_state state;
 210        unsigned long sz;
 211
 212        if (!cnt)
 213                return; /* result deleted */
 214
 215        parent_file.ptr = grab_blob(parent, &sz);
 216        parent_file.size = sz;
 217        xpp.flags = XDF_NEED_MINIMAL;
 218        memset(&xecfg, 0, sizeof(xecfg));
 219        ecb.outf = xdiff_outf;
 220        ecb.priv = &state;
 221        memset(&state, 0, sizeof(state));
 222        state.xm.consume = consume_line;
 223        state.nmask = nmask;
 224        state.sline = sline;
 225        state.lno = 1;
 226        state.num_parent = num_parent;
 227        state.n = n;
 228
 229        xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
 230        free(parent_file.ptr);
 231
 232        /* Assign line numbers for this parent.
 233         *
 234         * sline[lno].p_lno[n] records the first line number
 235         * (counting from 1) for parent N if the final hunk display
 236         * started by showing sline[lno] (possibly showing the lost
 237         * lines attached to it first).
 238         */
 239        for (lno = 0,  p_lno = 1; lno <= cnt; lno++) {
 240                struct lline *ll;
 241                sline[lno].p_lno[n] = p_lno;
 242
 243                /* How many lines would this sline advance the p_lno? */
 244                ll = sline[lno].lost_head;
 245                while (ll) {
 246                        if (ll->parent_map & nmask)
 247                                p_lno++; /* '-' means parent had it */
 248                        ll = ll->next;
 249                }
 250                if (lno < cnt && !(sline[lno].flag & nmask))
 251                        p_lno++; /* no '+' means parent had it */
 252        }
 253        sline[lno].p_lno[n] = p_lno; /* trailer */
 254}
 255
 256static unsigned long context = 3;
 257static char combine_marker = '@';
 258
 259static int interesting(struct sline *sline, unsigned long all_mask)
 260{
 261        /* If some parents lost lines here, or if we have added to
 262         * some parent, it is interesting.
 263         */
 264        return ((sline->flag & all_mask) || sline->lost_head);
 265}
 266
 267static unsigned long adjust_hunk_tail(struct sline *sline,
 268                                      unsigned long all_mask,
 269                                      unsigned long hunk_begin,
 270                                      unsigned long i)
 271{
 272        /* i points at the first uninteresting line.  If the last line
 273         * of the hunk was interesting only because it has some
 274         * deletion, then it is not all that interesting for the
 275         * purpose of giving trailing context lines.  This is because
 276         * we output '-' line and then unmodified sline[i-1] itself in
 277         * that case which gives us one extra context line.
 278         */
 279        if ((hunk_begin + 1 <= i) && !(sline[i-1].flag & all_mask))
 280                i--;
 281        return i;
 282}
 283
 284static unsigned long find_next(struct sline *sline,
 285                               unsigned long mark,
 286                               unsigned long i,
 287                               unsigned long cnt,
 288                               int look_for_uninteresting)
 289{
 290        /* We have examined up to i-1 and are about to look at i.
 291         * Find next interesting or uninteresting line.  Here,
 292         * "interesting" does not mean interesting(), but marked by
 293         * the give_context() function below (i.e. it includes context
 294         * lines that are not interesting to interesting() function
 295         * that are surrounded by interesting() ones.
 296         */
 297        while (i <= cnt)
 298                if (look_for_uninteresting
 299                    ? !(sline[i].flag & mark)
 300                    : (sline[i].flag & mark))
 301                        return i;
 302                else
 303                        i++;
 304        return i;
 305}
 306
 307static int give_context(struct sline *sline, unsigned long cnt, int num_parent)
 308{
 309        unsigned long all_mask = (1UL<<num_parent) - 1;
 310        unsigned long mark = (1UL<<num_parent);
 311        unsigned long i;
 312
 313        /* Two groups of interesting lines may have a short gap of
 314         * uninteresting lines.  Connect such groups to give them a
 315         * bit of context.
 316         *
 317         * We first start from what the interesting() function says,
 318         * and mark them with "mark", and paint context lines with the
 319         * mark.  So interesting() would still say false for such context
 320         * lines but they are treated as "interesting" in the end.
 321         */
 322        i = find_next(sline, mark, 0, cnt, 0);
 323        if (cnt < i)
 324                return 0;
 325
 326        while (i <= cnt) {
 327                unsigned long j = (context < i) ? (i - context) : 0;
 328                unsigned long k;
 329
 330                /* Paint a few lines before the first interesting line. */
 331                while (j < i)
 332                        sline[j++].flag |= mark;
 333
 334        again:
 335                /* we know up to i is to be included.  where does the
 336                 * next uninteresting one start?
 337                 */
 338                j = find_next(sline, mark, i, cnt, 1);
 339                if (cnt < j)
 340                        break; /* the rest are all interesting */
 341
 342                /* lookahead context lines */
 343                k = find_next(sline, mark, j, cnt, 0);
 344                j = adjust_hunk_tail(sline, all_mask, i, j);
 345
 346                if (k < j + context) {
 347                        /* k is interesting and [j,k) are not, but
 348                         * paint them interesting because the gap is small.
 349                         */
 350                        while (j < k)
 351                                sline[j++].flag |= mark;
 352                        i = k;
 353                        goto again;
 354                }
 355
 356                /* j is the first uninteresting line and there is
 357                 * no overlap beyond it within context lines.  Paint
 358                 * the trailing edge a bit.
 359                 */
 360                i = k;
 361                k = (j + context < cnt+1) ? j + context : cnt+1;
 362                while (j < k)
 363                        sline[j++].flag |= mark;
 364        }
 365        return 1;
 366}
 367
 368static int make_hunks(struct sline *sline, unsigned long cnt,
 369                       int num_parent, int dense)
 370{
 371        unsigned long all_mask = (1UL<<num_parent) - 1;
 372        unsigned long mark = (1UL<<num_parent);
 373        unsigned long i;
 374        int has_interesting = 0;
 375
 376        for (i = 0; i <= cnt; i++) {
 377                if (interesting(&sline[i], all_mask))
 378                        sline[i].flag |= mark;
 379                else
 380                        sline[i].flag &= ~mark;
 381        }
 382        if (!dense)
 383                return give_context(sline, cnt, num_parent);
 384
 385        /* Look at each hunk, and if we have changes from only one
 386         * parent, or the changes are the same from all but one
 387         * parent, mark that uninteresting.
 388         */
 389        i = 0;
 390        while (i <= cnt) {
 391                unsigned long j, hunk_begin, hunk_end;
 392                unsigned long same_diff;
 393                while (i <= cnt && !(sline[i].flag & mark))
 394                        i++;
 395                if (cnt < i)
 396                        break; /* No more interesting hunks */
 397                hunk_begin = i;
 398                for (j = i + 1; j <= cnt; j++) {
 399                        if (!(sline[j].flag & mark)) {
 400                                /* Look beyond the end to see if there
 401                                 * is an interesting line after this
 402                                 * hunk within context span.
 403                                 */
 404                                unsigned long la; /* lookahead */
 405                                int contin = 0;
 406                                la = adjust_hunk_tail(sline, all_mask,
 407                                                     hunk_begin, j);
 408                                la = (la + context < cnt + 1) ?
 409                                        (la + context) : cnt + 1;
 410                                while (j <= --la) {
 411                                        if (sline[la].flag & mark) {
 412                                                contin = 1;
 413                                                break;
 414                                        }
 415                                }
 416                                if (!contin)
 417                                        break;
 418                                j = la;
 419                        }
 420                }
 421                hunk_end = j;
 422
 423                /* [i..hunk_end) are interesting.  Now is it really
 424                 * interesting?  We check if there are only two versions
 425                 * and the result matches one of them.  That is, we look
 426                 * at:
 427                 *   (+) line, which records lines added to which parents;
 428                 *       this line appears in the result.
 429                 *   (-) line, which records from what parents the line
 430                 *       was removed; this line does not appear in the result.
 431                 * then check the set of parents the result has difference
 432                 * from, from all lines.  If there are lines that has
 433                 * different set of parents that the result has differences
 434                 * from, that means we have more than two versions.
 435                 *
 436                 * Even when we have only two versions, if the result does
 437                 * not match any of the parents, the it should be considered
 438                 * interesting.  In such a case, we would have all '+' line.
 439                 * After passing the above "two versions" test, that would
 440                 * appear as "the same set of parents" to be "all parents".
 441                 */
 442                same_diff = 0;
 443                has_interesting = 0;
 444                for (j = i; j < hunk_end && !has_interesting; j++) {
 445                        unsigned long this_diff = sline[j].flag & all_mask;
 446                        struct lline *ll = sline[j].lost_head;
 447                        if (this_diff) {
 448                                /* This has some changes.  Is it the
 449                                 * same as others?
 450                                 */
 451                                if (!same_diff)
 452                                        same_diff = this_diff;
 453                                else if (same_diff != this_diff) {
 454                                        has_interesting = 1;
 455                                        break;
 456                                }
 457                        }
 458                        while (ll && !has_interesting) {
 459                                /* Lost this line from these parents;
 460                                 * who are they?  Are they the same?
 461                                 */
 462                                this_diff = ll->parent_map;
 463                                if (!same_diff)
 464                                        same_diff = this_diff;
 465                                else if (same_diff != this_diff) {
 466                                        has_interesting = 1;
 467                                }
 468                                ll = ll->next;
 469                        }
 470                }
 471
 472                if (!has_interesting && same_diff != all_mask) {
 473                        /* This hunk is not that interesting after all */
 474                        for (j = hunk_begin; j < hunk_end; j++)
 475                                sline[j].flag &= ~mark;
 476                }
 477                i = hunk_end;
 478        }
 479
 480        has_interesting = give_context(sline, cnt, num_parent);
 481        return has_interesting;
 482}
 483
 484static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n, unsigned long null_context)
 485{
 486        l0 = sline[l0].p_lno[n];
 487        l1 = sline[l1].p_lno[n];
 488        printf(" -%lu,%lu", l0, l1-l0-null_context);
 489}
 490
 491static int hunk_comment_line(const char *bol)
 492{
 493        int ch;
 494
 495        if (!bol)
 496                return 0;
 497        ch = *bol & 0xff;
 498        return (isalpha(ch) || ch == '_' || ch == '$');
 499}
 500
 501static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
 502                       int use_color)
 503{
 504        unsigned long mark = (1UL<<num_parent);
 505        int i;
 506        unsigned long lno = 0;
 507        const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
 508        const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
 509        const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
 510        const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
 511        const char *c_reset = diff_get_color(use_color, DIFF_RESET);
 512
 513        if (!cnt)
 514                return; /* result deleted */
 515
 516        while (1) {
 517                struct sline *sl = &sline[lno];
 518                unsigned long hunk_end;
 519                unsigned long rlines;
 520                const char *hunk_comment = NULL;
 521                unsigned long null_context = 0;
 522
 523                while (lno <= cnt && !(sline[lno].flag & mark)) {
 524                        if (hunk_comment_line(sline[lno].bol))
 525                                hunk_comment = sline[lno].bol;
 526                        lno++;
 527                }
 528                if (cnt < lno)
 529                        break;
 530                else {
 531                        for (hunk_end = lno + 1; hunk_end <= cnt; hunk_end++)
 532                                if (!(sline[hunk_end].flag & mark))
 533                                        break;
 534                }
 535                rlines = hunk_end - lno;
 536                if (cnt < hunk_end)
 537                        rlines--; /* pointing at the last delete hunk */
 538
 539                if (!context) {
 540                        /*
 541                         * Even when running with --unified=0, all
 542                         * lines in the hunk needs to be processed in
 543                         * the loop below in order to show the
 544                         * deletion recorded in lost_head.  However,
 545                         * we do not want to show the resulting line
 546                         * with all blank context markers in such a
 547                         * case.  Compensate.
 548                         */
 549                        unsigned long j;
 550                        for (j = lno; j < hunk_end; j++)
 551                                if (!(sline[j].flag & (mark-1)))
 552                                        null_context++;
 553                        rlines -= null_context;
 554                }
 555
 556                fputs(c_frag, stdout);
 557                for (i = 0; i <= num_parent; i++) putchar(combine_marker);
 558                for (i = 0; i < num_parent; i++)
 559                        show_parent_lno(sline, lno, hunk_end, i, null_context);
 560                printf(" +%lu,%lu ", lno+1, rlines);
 561                for (i = 0; i <= num_parent; i++) putchar(combine_marker);
 562
 563                if (hunk_comment) {
 564                        int comment_end = 0;
 565                        for (i = 0; i < 40; i++) {
 566                                int ch = hunk_comment[i] & 0xff;
 567                                if (!ch || ch == '\n')
 568                                        break;
 569                                if (!isspace(ch))
 570                                    comment_end = i;
 571                        }
 572                        if (comment_end)
 573                                putchar(' ');
 574                        for (i = 0; i < comment_end; i++)
 575                                putchar(hunk_comment[i]);
 576                }
 577
 578                printf("%s\n", c_reset);
 579                while (lno < hunk_end) {
 580                        struct lline *ll;
 581                        int j;
 582                        unsigned long p_mask;
 583                        sl = &sline[lno++];
 584                        ll = sl->lost_head;
 585                        while (ll) {
 586                                fputs(c_old, stdout);
 587                                for (j = 0; j < num_parent; j++) {
 588                                        if (ll->parent_map & (1UL<<j))
 589                                                putchar('-');
 590                                        else
 591                                                putchar(' ');
 592                                }
 593                                printf("%s%s\n", ll->line, c_reset);
 594                                ll = ll->next;
 595                        }
 596                        if (cnt < lno)
 597                                break;
 598                        p_mask = 1;
 599                        if (!(sl->flag & (mark-1))) {
 600                                /*
 601                                 * This sline was here to hang the
 602                                 * lost lines in front of it.
 603                                 */
 604                                if (!context)
 605                                        continue;
 606                                fputs(c_plain, stdout);
 607                        }
 608                        else
 609                                fputs(c_new, stdout);
 610                        for (j = 0; j < num_parent; j++) {
 611                                if (p_mask & sl->flag)
 612                                        putchar('+');
 613                                else
 614                                        putchar(' ');
 615                                p_mask <<= 1;
 616                        }
 617                        printf("%.*s%s\n", sl->len, sl->bol, c_reset);
 618                }
 619        }
 620}
 621
 622static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
 623                               int i, int j)
 624{
 625        /* We have already examined parent j and we know parent i
 626         * and parent j are the same, so reuse the combined result
 627         * of parent j for parent i.
 628         */
 629        unsigned long lno, imask, jmask;
 630        imask = (1UL<<i);
 631        jmask = (1UL<<j);
 632
 633        for (lno = 0; lno <= cnt; lno++) {
 634                struct lline *ll = sline->lost_head;
 635                sline->p_lno[i] = sline->p_lno[j];
 636                while (ll) {
 637                        if (ll->parent_map & jmask)
 638                                ll->parent_map |= imask;
 639                        ll = ll->next;
 640                }
 641                if (sline->flag & jmask)
 642                        sline->flag |= imask;
 643                sline++;
 644        }
 645        /* the overall size of the file (sline[cnt]) */
 646        sline->p_lno[i] = sline->p_lno[j];
 647}
 648
 649static void dump_quoted_path(const char *head,
 650                             const char *prefix,
 651                             const char *path,
 652                             const char *c_meta, const char *c_reset)
 653{
 654        static struct strbuf buf = STRBUF_INIT;
 655
 656        strbuf_reset(&buf);
 657        strbuf_addstr(&buf, c_meta);
 658        strbuf_addstr(&buf, head);
 659        quote_two_c_style(&buf, prefix, path, 0);
 660        strbuf_addstr(&buf, c_reset);
 661        puts(buf.buf);
 662}
 663
 664static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 665                            int dense, struct rev_info *rev)
 666{
 667        struct diff_options *opt = &rev->diffopt;
 668        unsigned long result_size, cnt, lno;
 669        char *result, *cp;
 670        struct sline *sline; /* survived lines */
 671        int mode_differs = 0;
 672        int i, show_hunks;
 673        int working_tree_file = is_null_sha1(elem->sha1);
 674        int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
 675        mmfile_t result_file;
 676
 677        context = opt->context;
 678        /* Read the result of merge first */
 679        if (!working_tree_file)
 680                result = grab_blob(elem->sha1, &result_size);
 681        else {
 682                /* Used by diff-tree to read from the working tree */
 683                struct stat st;
 684                int fd = -1;
 685
 686                if (lstat(elem->path, &st) < 0)
 687                        goto deleted_file;
 688
 689                if (S_ISLNK(st.st_mode)) {
 690                        size_t len = xsize_t(st.st_size);
 691                        result_size = len;
 692                        result = xmalloc(len + 1);
 693                        if (result_size != readlink(elem->path, result, len)) {
 694                                error("readlink(%s): %s", elem->path,
 695                                      strerror(errno));
 696                                return;
 697                        }
 698                        result[len] = 0;
 699                        elem->mode = canon_mode(st.st_mode);
 700                }
 701                else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
 702                         !fstat(fd, &st)) {
 703                        size_t len = xsize_t(st.st_size);
 704                        size_t sz = 0;
 705                        int is_file, i;
 706
 707                        elem->mode = canon_mode(st.st_mode);
 708                        /* if symlinks don't work, assume symlink if all parents
 709                         * are symlinks
 710                         */
 711                        is_file = has_symlinks;
 712                        for (i = 0; !is_file && i < num_parent; i++)
 713                                is_file = !S_ISLNK(elem->parent[i].mode);
 714                        if (!is_file)
 715                                elem->mode = canon_mode(S_IFLNK);
 716
 717                        result_size = len;
 718                        result = xmalloc(len + 1);
 719                        while (sz < len) {
 720                                ssize_t done = xread(fd, result+sz, len-sz);
 721                                if (done == 0)
 722                                        break;
 723                                if (done < 0)
 724                                        die("read error '%s'", elem->path);
 725                                sz += done;
 726                        }
 727                        result[len] = 0;
 728                }
 729                else {
 730                deleted_file:
 731                        result_size = 0;
 732                        elem->mode = 0;
 733                        result = xcalloc(1, 1);
 734                }
 735
 736                if (0 <= fd)
 737                        close(fd);
 738        }
 739
 740        for (cnt = 0, cp = result; cp < result + result_size; cp++) {
 741                if (*cp == '\n')
 742                        cnt++;
 743        }
 744        if (result_size && result[result_size-1] != '\n')
 745                cnt++; /* incomplete line */
 746
 747        sline = xcalloc(cnt+2, sizeof(*sline));
 748        sline[0].bol = result;
 749        for (lno = 0; lno <= cnt + 1; lno++) {
 750                sline[lno].lost_tail = &sline[lno].lost_head;
 751                sline[lno].flag = 0;
 752        }
 753        for (lno = 0, cp = result; cp < result + result_size; cp++) {
 754                if (*cp == '\n') {
 755                        sline[lno].len = cp - sline[lno].bol;
 756                        lno++;
 757                        if (lno < cnt)
 758                                sline[lno].bol = cp + 1;
 759                }
 760        }
 761        if (result_size && result[result_size-1] != '\n')
 762                sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
 763
 764        result_file.ptr = result;
 765        result_file.size = result_size;
 766
 767        /* Even p_lno[cnt+1] is valid -- that is for the end line number
 768         * for deletion hunk at the end.
 769         */
 770        sline[0].p_lno = xcalloc((cnt+2) * num_parent, sizeof(unsigned long));
 771        for (lno = 0; lno <= cnt; lno++)
 772                sline[lno+1].p_lno = sline[lno].p_lno + num_parent;
 773
 774        for (i = 0; i < num_parent; i++) {
 775                int j;
 776                for (j = 0; j < i; j++) {
 777                        if (!hashcmp(elem->parent[i].sha1,
 778                                     elem->parent[j].sha1)) {
 779                                reuse_combine_diff(sline, cnt, i, j);
 780                                break;
 781                        }
 782                }
 783                if (i <= j)
 784                        combine_diff(elem->parent[i].sha1, &result_file, sline,
 785                                     cnt, i, num_parent);
 786                if (elem->parent[i].mode != elem->mode)
 787                        mode_differs = 1;
 788        }
 789
 790        show_hunks = make_hunks(sline, cnt, num_parent, dense);
 791
 792        if (show_hunks || mode_differs || working_tree_file) {
 793                const char *abb;
 794                int use_color = DIFF_OPT_TST(opt, COLOR_DIFF);
 795                const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
 796                const char *c_reset = diff_get_color(use_color, DIFF_RESET);
 797                int added = 0;
 798                int deleted = 0;
 799
 800                if (rev->loginfo && !rev->no_commit_id)
 801                        show_log(rev, opt->msg_sep);
 802                dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
 803                                 "", elem->path, c_meta, c_reset);
 804                printf("%sindex ", c_meta);
 805                for (i = 0; i < num_parent; i++) {
 806                        abb = find_unique_abbrev(elem->parent[i].sha1,
 807                                                 abbrev);
 808                        printf("%s%s", i ? "," : "", abb);
 809                }
 810                abb = find_unique_abbrev(elem->sha1, abbrev);
 811                printf("..%s%s\n", abb, c_reset);
 812
 813                if (mode_differs) {
 814                        deleted = !elem->mode;
 815
 816                        /* We say it was added if nobody had it */
 817                        added = !deleted;
 818                        for (i = 0; added && i < num_parent; i++)
 819                                if (elem->parent[i].status !=
 820                                    DIFF_STATUS_ADDED)
 821                                        added = 0;
 822                        if (added)
 823                                printf("%snew file mode %06o",
 824                                       c_meta, elem->mode);
 825                        else {
 826                                if (deleted)
 827                                        printf("%sdeleted file ", c_meta);
 828                                printf("mode ");
 829                                for (i = 0; i < num_parent; i++) {
 830                                        printf("%s%06o", i ? "," : "",
 831                                               elem->parent[i].mode);
 832                                }
 833                                if (elem->mode)
 834                                        printf("..%06o", elem->mode);
 835                        }
 836                        printf("%s\n", c_reset);
 837                }
 838                if (added)
 839                        dump_quoted_path("--- ", "", "/dev/null",
 840                                         c_meta, c_reset);
 841                else
 842                        dump_quoted_path("--- ", opt->a_prefix, elem->path,
 843                                         c_meta, c_reset);
 844                if (deleted)
 845                        dump_quoted_path("+++ ", "", "/dev/null",
 846                                         c_meta, c_reset);
 847                else
 848                        dump_quoted_path("+++ ", opt->b_prefix, elem->path,
 849                                         c_meta, c_reset);
 850                dump_sline(sline, cnt, num_parent,
 851                           DIFF_OPT_TST(opt, COLOR_DIFF));
 852        }
 853        free(result);
 854
 855        for (lno = 0; lno < cnt; lno++) {
 856                if (sline[lno].lost_head) {
 857                        struct lline *ll = sline[lno].lost_head;
 858                        while (ll) {
 859                                struct lline *tmp = ll;
 860                                ll = ll->next;
 861                                free(tmp);
 862                        }
 863                }
 864        }
 865        free(sline[0].p_lno);
 866        free(sline);
 867}
 868
 869#define COLONS "::::::::::::::::::::::::::::::::"
 870
 871static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)
 872{
 873        struct diff_options *opt = &rev->diffopt;
 874        int i, offset;
 875        const char *prefix;
 876        int line_termination, inter_name_termination;
 877
 878        line_termination = opt->line_termination;
 879        inter_name_termination = '\t';
 880        if (!line_termination)
 881                inter_name_termination = 0;
 882
 883        if (rev->loginfo && !rev->no_commit_id)
 884                show_log(rev, opt->msg_sep);
 885
 886        if (opt->output_format & DIFF_FORMAT_RAW) {
 887                offset = strlen(COLONS) - num_parent;
 888                if (offset < 0)
 889                        offset = 0;
 890                prefix = COLONS + offset;
 891
 892                /* Show the modes */
 893                for (i = 0; i < num_parent; i++) {
 894                        printf("%s%06o", prefix, p->parent[i].mode);
 895                        prefix = " ";
 896                }
 897                printf("%s%06o", prefix, p->mode);
 898
 899                /* Show sha1's */
 900                for (i = 0; i < num_parent; i++)
 901                        printf(" %s", diff_unique_abbrev(p->parent[i].sha1,
 902                                                         opt->abbrev));
 903                printf(" %s ", diff_unique_abbrev(p->sha1, opt->abbrev));
 904        }
 905
 906        if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
 907                for (i = 0; i < num_parent; i++)
 908                        putchar(p->parent[i].status);
 909                putchar(inter_name_termination);
 910        }
 911
 912        write_name_quoted(p->path, stdout, line_termination);
 913}
 914
 915void show_combined_diff(struct combine_diff_path *p,
 916                       int num_parent,
 917                       int dense,
 918                       struct rev_info *rev)
 919{
 920        struct diff_options *opt = &rev->diffopt;
 921        if (!p->len)
 922                return;
 923        if (opt->output_format & (DIFF_FORMAT_RAW |
 924                                  DIFF_FORMAT_NAME |
 925                                  DIFF_FORMAT_NAME_STATUS))
 926                show_raw_diff(p, num_parent, rev);
 927        else if (opt->output_format & DIFF_FORMAT_PATCH)
 928                show_patch_diff(p, num_parent, dense, rev);
 929}
 930
 931void diff_tree_combined(const unsigned char *sha1,
 932                        const unsigned char parent[][20],
 933                        int num_parent,
 934                        int dense,
 935                        struct rev_info *rev)
 936{
 937        struct diff_options *opt = &rev->diffopt;
 938        struct diff_options diffopts;
 939        struct combine_diff_path *p, *paths = NULL;
 940        int i, num_paths, needsep, show_log_first;
 941
 942        diffopts = *opt;
 943        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
 944        DIFF_OPT_SET(&diffopts, RECURSIVE);
 945        DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
 946
 947        show_log_first = !!rev->loginfo && !rev->no_commit_id;
 948        needsep = 0;
 949        /* find set of paths that everybody touches */
 950        for (i = 0; i < num_parent; i++) {
 951                /* show stat against the first parent even
 952                 * when doing combined diff.
 953                 */
 954                int stat_opt = (opt->output_format &
 955                                (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
 956                if (i == 0 && stat_opt)
 957                        diffopts.output_format = stat_opt;
 958                else
 959                        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
 960                diff_tree_sha1(parent[i], sha1, "", &diffopts);
 961                diffcore_std(&diffopts);
 962                paths = intersect_paths(paths, i, num_parent);
 963
 964                if (show_log_first && i == 0) {
 965                        show_log(rev, opt->msg_sep);
 966                        if (rev->verbose_header && opt->output_format)
 967                                putchar(opt->line_termination);
 968                }
 969                diff_flush(&diffopts);
 970        }
 971
 972        /* find out surviving paths */
 973        for (num_paths = 0, p = paths; p; p = p->next) {
 974                if (p->len)
 975                        num_paths++;
 976        }
 977        if (num_paths) {
 978                if (opt->output_format & (DIFF_FORMAT_RAW |
 979                                          DIFF_FORMAT_NAME |
 980                                          DIFF_FORMAT_NAME_STATUS)) {
 981                        for (p = paths; p; p = p->next) {
 982                                if (p->len)
 983                                        show_raw_diff(p, num_parent, rev);
 984                        }
 985                        needsep = 1;
 986                }
 987                else if (opt->output_format &
 988                         (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
 989                        needsep = 1;
 990                if (opt->output_format & DIFF_FORMAT_PATCH) {
 991                        if (needsep)
 992                                putchar(opt->line_termination);
 993                        for (p = paths; p; p = p->next) {
 994                                if (p->len)
 995                                        show_patch_diff(p, num_parent, dense,
 996                                                        rev);
 997                        }
 998                }
 999        }
1000
1001        /* Clean things up */
1002        while (paths) {
1003                struct combine_diff_path *tmp = paths;
1004                paths = paths->next;
1005                free(tmp);
1006        }
1007}
1008
1009void diff_tree_combined_merge(const unsigned char *sha1,
1010                             int dense, struct rev_info *rev)
1011{
1012        int num_parent;
1013        const unsigned char (*parent)[20];
1014        struct commit *commit = lookup_commit(sha1);
1015        struct commit_list *parents;
1016
1017        /* count parents */
1018        for (parents = commit->parents, num_parent = 0;
1019             parents;
1020             parents = parents->next, num_parent++)
1021                ; /* nothing */
1022
1023        parent = xmalloc(num_parent * sizeof(*parent));
1024        for (parents = commit->parents, num_parent = 0;
1025             parents;
1026             parents = parents->next, num_parent++)
1027                hashcpy((unsigned char*)(parent + num_parent),
1028                        parents->item->object.sha1);
1029        diff_tree_combined(sha1, parent, num_parent, dense, rev);
1030}