1#include "cache.h" 2#include "commit.h" 3#include "diff.h" 4#include "diffcore.h" 5#include "quote.h" 6 7static int uninteresting(struct diff_filepair *p) 8{ 9 if (diff_unmodified_pair(p)) 10 return 1; 11 if (!S_ISREG(p->one->mode) || !S_ISREG(p->two->mode)) 12 return 1; 13 return 0; 14} 15 16static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent) 17{ 18 struct diff_queue_struct *q = &diff_queued_diff; 19 struct combine_diff_path *p; 20 int i; 21 22 if (!n) { 23 struct combine_diff_path *list = NULL, **tail = &list; 24 for (i = 0; i < q->nr; i++) { 25 int len; 26 const char *path; 27 if (uninteresting(q->queue[i])) 28 continue; 29 path = q->queue[i]->two->path; 30 len = strlen(path); 31 p = xmalloc(combine_diff_path_size(num_parent, len)); 32 p->path = (char*) &(p->parent[num_parent]); 33 memcpy(p->path, path, len); 34 p->path[len] = 0; 35 p->len = len; 36 p->next = NULL; 37 memset(p->parent, 0, 38 sizeof(p->parent[0]) * num_parent); 39 40 memcpy(p->sha1, q->queue[i]->two->sha1, 20); 41 p->mode = q->queue[i]->two->mode; 42 memcpy(p->parent[n].sha1, q->queue[i]->one->sha1, 20); 43 p->parent[n].mode = q->queue[i]->one->mode; 44 *tail = p; 45 tail = &p->next; 46 } 47 return list; 48 } 49 50 for (p = curr; p; p = p->next) { 51 int found = 0; 52 if (!p->len) 53 continue; 54 for (i = 0; i < q->nr; i++) { 55 const char *path; 56 int len; 57 58 if (uninteresting(q->queue[i])) 59 continue; 60 path = q->queue[i]->two->path; 61 len = strlen(path); 62 if (len == p->len && !memcmp(path, p->path, len)) { 63 found = 1; 64 memcpy(p->parent[n].sha1, 65 q->queue[i]->one->sha1, 20); 66 p->parent[n].mode = q->queue[i]->one->mode; 67 break; 68 } 69 } 70 if (!found) 71 p->len = 0; 72 } 73 return curr; 74} 75 76/* Lines lost from parent */ 77struct lline { 78 struct lline *next; 79 int len; 80 unsigned long parent_map; 81 char line[FLEX_ARRAY]; 82}; 83 84/* Lines surviving in the merge result */ 85struct sline { 86 struct lline *lost_head, **lost_tail; 87 char *bol; 88 int len; 89 /* bit 0 up to (N-1) are on if the parent has this line (i.e. 90 * we did not change it). 91 * bit N is used for "interesting" lines, including context. 92 */ 93 unsigned long flag; 94 unsigned long *p_lno; 95}; 96 97static char *grab_blob(const unsigned char *sha1, unsigned long *size) 98{ 99 char *blob; 100 char type[20]; 101 if (!memcmp(sha1, null_sha1, 20)) { 102 /* deleted blob */ 103 *size = 0; 104 return xcalloc(1, 1); 105 } 106 blob = read_sha1_file(sha1, type, size); 107 if (strcmp(type, "blob")) 108 die("object '%s' is not a blob!", sha1_to_hex(sha1)); 109 return blob; 110} 111 112#define TMPPATHLEN 50 113#define MAXLINELEN 10240 114 115static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size) 116{ 117 int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX"); 118 if (fd < 0) 119 die("unable to create temp-file"); 120 if (write(fd, blob, size) != size) 121 die("unable to write temp-file"); 122 close(fd); 123} 124 125static void write_temp_blob(char *tmpfile, const unsigned char *sha1) 126{ 127 unsigned long size; 128 void *blob; 129 blob = grab_blob(sha1, &size); 130 write_to_temp_file(tmpfile, blob, size); 131 free(blob); 132} 133 134static int parse_num(char **cp_p, unsigned int *num_p) 135{ 136 char *cp = *cp_p; 137 unsigned int num = 0; 138 int read_some; 139 140 while ('0' <= *cp && *cp <= '9') 141 num = num * 10 + *cp++ - '0'; 142 if (!(read_some = cp - *cp_p)) 143 return -1; 144 *cp_p = cp; 145 *num_p = num; 146 return 0; 147} 148 149static int parse_hunk_header(char *line, int len, 150 unsigned int *ob, unsigned int *on, 151 unsigned int *nb, unsigned int *nn) 152{ 153 char *cp; 154 cp = line + 4; 155 if (parse_num(&cp, ob)) { 156 bad_line: 157 return error("malformed diff output: %s", line); 158 } 159 if (*cp == ',') { 160 cp++; 161 if (parse_num(&cp, on)) 162 goto bad_line; 163 } 164 else 165 *on = 1; 166 if (*cp++ != ' ' || *cp++ != '+') 167 goto bad_line; 168 if (parse_num(&cp, nb)) 169 goto bad_line; 170 if (*cp == ',') { 171 cp++; 172 if (parse_num(&cp, nn)) 173 goto bad_line; 174 } 175 else 176 *nn = 1; 177 return -!!memcmp(cp, " @@", 3); 178} 179 180static void append_lost(struct sline *sline, int n, const char *line) 181{ 182 struct lline *lline; 183 int len = strlen(line); 184 unsigned long this_mask = (1UL<<n); 185 if (line[len-1] == '\n') 186 len--; 187 188 /* Check to see if we can squash things */ 189 if (sline->lost_head) { 190 struct lline *last_one = NULL; 191 /* We cannot squash it with earlier one */ 192 for (lline = sline->lost_head; 193 lline; 194 lline = lline->next) 195 if (lline->parent_map & this_mask) 196 last_one = lline; 197 lline = last_one ? last_one->next : sline->lost_head; 198 while (lline) { 199 if (lline->len == len && 200 !memcmp(lline->line, line, len)) { 201 lline->parent_map |= this_mask; 202 return; 203 } 204 lline = lline->next; 205 } 206 } 207 208 lline = xmalloc(sizeof(*lline) + len + 1); 209 lline->len = len; 210 lline->next = NULL; 211 lline->parent_map = this_mask; 212 memcpy(lline->line, line, len); 213 lline->line[len] = 0; 214 *sline->lost_tail = lline; 215 sline->lost_tail = &lline->next; 216} 217 218static void combine_diff(const unsigned char *parent, const char *ourtmp, 219 struct sline *sline, int cnt, int n, int num_parent) 220{ 221 FILE *in; 222 char parent_tmp[TMPPATHLEN]; 223 char cmd[TMPPATHLEN * 2 + 1024]; 224 char line[MAXLINELEN]; 225 unsigned int lno, ob, on, nb, nn, p_lno; 226 unsigned long nmask = (1UL << n); 227 struct sline *lost_bucket = NULL; 228 229 write_temp_blob(parent_tmp, parent); 230 sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'", 231 parent_tmp, ourtmp); 232 in = popen(cmd, "r"); 233 if (!in) 234 die("cannot spawn %s", cmd); 235 236 lno = 1; 237 while (fgets(line, sizeof(line), in) != NULL) { 238 int len = strlen(line); 239 if (5 < len && !memcmp("@@ -", line, 4)) { 240 if (parse_hunk_header(line, len, 241 &ob, &on, &nb, &nn)) 242 break; 243 lno = nb; 244 if (!nb) 245 /* @@ -1,2 +0,0 @@ to remove the 246 * first two lines... 247 */ 248 nb = 1; 249 if (nn == 0) 250 /* @@ -X,Y +N,0 @@ removed Y lines 251 * that would have come *after* line N 252 * in the result. Our lost buckets hang 253 * to the line after the removed lines, 254 */ 255 lost_bucket = &sline[nb]; 256 else 257 lost_bucket = &sline[nb-1]; 258 if (!sline[nb-1].p_lno) 259 sline[nb-1].p_lno = 260 xcalloc(num_parent, 261 sizeof(unsigned long)); 262 sline[nb-1].p_lno[n] = ob; 263 continue; 264 } 265 if (!lost_bucket) 266 continue; /* not in any hunk yet */ 267 switch (line[0]) { 268 case '-': 269 append_lost(lost_bucket, n, line+1); 270 break; 271 case '+': 272 sline[lno-1].flag |= nmask; 273 lno++; 274 break; 275 } 276 } 277 fclose(in); 278 unlink(parent_tmp); 279 280 /* Assign line numbers for this parent. 281 * 282 * sline[lno].p_lno[n] records the first line number 283 * (counting from 1) for parent N if the final hunk display 284 * started by showing sline[lno] (possibly showing the lost 285 * lines attached to it first). 286 */ 287 for (lno = 0, p_lno = 1; lno < cnt; lno++) { 288 struct lline *ll; 289 sline[lno].p_lno[n] = p_lno; 290 291 /* How many lines would this sline advance the p_lno? */ 292 ll = sline[lno].lost_head; 293 while (ll) { 294 if (ll->parent_map & nmask) 295 p_lno++; /* '-' means parent had it */ 296 ll = ll->next; 297 } 298 if (!(sline[lno].flag & nmask)) 299 p_lno++; /* no '+' means parent had it */ 300 } 301 sline[lno].p_lno[n] = p_lno; /* trailer */ 302} 303 304static unsigned long context = 3; 305static char combine_marker = '@'; 306 307static int interesting(struct sline *sline, unsigned long all_mask) 308{ 309 /* If some parents lost lines here, or if we have added to 310 * some parent, it is interesting. 311 */ 312 return ((sline->flag & all_mask) || sline->lost_head); 313} 314 315static unsigned long adjust_hunk_tail(struct sline *sline, 316 unsigned long all_mask, 317 unsigned long hunk_begin, 318 unsigned long i) 319{ 320 /* i points at the first uninteresting line. If the last line 321 * of the hunk was interesting only because it has some 322 * deletion, then it is not all that interesting for the 323 * purpose of giving trailing context lines. This is because 324 * we output '-' line and then unmodified sline[i-1] itself in 325 * that case which gives us one extra context line. 326 */ 327 if ((hunk_begin + 1 <= i) && !(sline[i-1].flag & all_mask)) 328 i--; 329 return i; 330} 331 332static unsigned long find_next(struct sline *sline, 333 unsigned long mark, 334 unsigned long i, 335 unsigned long cnt, 336 int uninteresting) 337{ 338 /* We have examined up to i-1 and are about to look at i. 339 * Find next interesting or uninteresting line. Here, 340 * "interesting" does not mean interesting(), but marked by 341 * the give_context() function below (i.e. it includes context 342 * lines that are not interesting to interesting() function 343 * that are surrounded by interesting() ones. 344 */ 345 while (i < cnt) 346 if (uninteresting 347 ? !(sline[i].flag & mark) 348 : (sline[i].flag & mark)) 349 return i; 350 else 351 i++; 352 return cnt; 353} 354 355static int give_context(struct sline *sline, unsigned long cnt, int num_parent) 356{ 357 unsigned long all_mask = (1UL<<num_parent) - 1; 358 unsigned long mark = (1UL<<num_parent); 359 unsigned long i; 360 361 /* Two groups of interesting lines may have a short gap of 362 * unintersting lines. Connect such groups to give them a 363 * bit of context. 364 * 365 * We first start from what the interesting() function says, 366 * and mark them with "mark", and paint context lines with the 367 * mark. So interesting() would still say false for such context 368 * lines but they are treated as "interesting" in the end. 369 */ 370 i = find_next(sline, mark, 0, cnt, 0); 371 if (cnt <= i) 372 return 0; 373 374 while (i < cnt) { 375 unsigned long j = (context < i) ? (i - context) : 0; 376 unsigned long k; 377 378 /* Paint a few lines before the first interesting line. */ 379 while (j < i) 380 sline[j++].flag |= mark; 381 382 again: 383 /* we know up to i is to be included. where does the 384 * next uninteresting one start? 385 */ 386 j = find_next(sline, mark, i, cnt, 1); 387 if (cnt <= j) 388 break; /* the rest are all interesting */ 389 390 /* lookahead context lines */ 391 k = find_next(sline, mark, j, cnt, 0); 392 j = adjust_hunk_tail(sline, all_mask, i, j); 393 394 if (k < j + context) { 395 /* k is interesting and [j,k) are not, but 396 * paint them interesting because the gap is small. 397 */ 398 while (j < k) 399 sline[j++].flag |= mark; 400 i = k; 401 goto again; 402 } 403 404 /* j is the first uninteresting line and there is 405 * no overlap beyond it within context lines. Paint 406 * the trailing edge a bit. 407 */ 408 i = k; 409 k = (j + context < cnt) ? j + context : cnt; 410 while (j < k) 411 sline[j++].flag |= mark; 412 } 413 return 1; 414} 415 416static int make_hunks(struct sline *sline, unsigned long cnt, 417 int num_parent, int dense) 418{ 419 unsigned long all_mask = (1UL<<num_parent) - 1; 420 unsigned long mark = (1UL<<num_parent); 421 unsigned long i; 422 int has_interesting = 0; 423 424 for (i = 0; i < cnt; i++) { 425 if (interesting(&sline[i], all_mask)) 426 sline[i].flag |= mark; 427 else 428 sline[i].flag &= ~mark; 429 } 430 if (!dense) 431 return give_context(sline, cnt, num_parent); 432 433 /* Look at each hunk, and if we have changes from only one 434 * parent, or the changes are the same from all but one 435 * parent, mark that uninteresting. 436 */ 437 i = 0; 438 while (i < cnt) { 439 unsigned long j, hunk_begin, hunk_end; 440 unsigned long same_diff; 441 while (i < cnt && !(sline[i].flag & mark)) 442 i++; 443 if (cnt <= i) 444 break; /* No more interesting hunks */ 445 hunk_begin = i; 446 for (j = i + 1; j < cnt; j++) { 447 if (!(sline[j].flag & mark)) { 448 /* Look beyond the end to see if there 449 * is an interesting line after this 450 * hunk within context span. 451 */ 452 unsigned long la; /* lookahead */ 453 int contin = 0; 454 la = adjust_hunk_tail(sline, all_mask, 455 hunk_begin, j); 456 la = (la + context < cnt) ? 457 (la + context) : cnt; 458 while (j <= --la) { 459 if (sline[la].flag & mark) { 460 contin = 1; 461 break; 462 } 463 } 464 if (!contin) 465 break; 466 j = la; 467 } 468 } 469 hunk_end = j; 470 471 /* [i..hunk_end) are interesting. Now is it really 472 * interesting? We check if there are only two versions 473 * and the result matches one of them. That is, we look 474 * at: 475 * (+) line, which records lines added to which parents; 476 * this line appears in the result. 477 * (-) line, which records from what parents the line 478 * was removed; this line does not appear in the result. 479 * then check the set of parents the result has difference 480 * from, from all lines. If there are lines that has 481 * different set of parents that the result has differences 482 * from, that means we have more than two versions. 483 * 484 * Even when we have only two versions, if the result does 485 * not match any of the parents, the it should be considered 486 * interesting. In such a case, we would have all '+' line. 487 * After passing the above "two versions" test, that would 488 * appear as "the same set of parents" to be "all parents". 489 */ 490 same_diff = 0; 491 has_interesting = 0; 492 for (j = i; j < hunk_end && !has_interesting; j++) { 493 unsigned long this_diff = sline[j].flag & all_mask; 494 struct lline *ll = sline[j].lost_head; 495 if (this_diff) { 496 /* This has some changes. Is it the 497 * same as others? 498 */ 499 if (!same_diff) 500 same_diff = this_diff; 501 else if (same_diff != this_diff) { 502 has_interesting = 1; 503 break; 504 } 505 } 506 while (ll && !has_interesting) { 507 /* Lost this line from these parents; 508 * who are they? Are they the same? 509 */ 510 this_diff = ll->parent_map; 511 if (!same_diff) 512 same_diff = this_diff; 513 else if (same_diff != this_diff) { 514 has_interesting = 1; 515 } 516 ll = ll->next; 517 } 518 } 519 520 if (!has_interesting && same_diff != all_mask) { 521 /* This hunk is not that interesting after all */ 522 for (j = hunk_begin; j < hunk_end; j++) 523 sline[j].flag &= ~mark; 524 } 525 i = hunk_end; 526 } 527 528 has_interesting = give_context(sline, cnt, num_parent); 529 return has_interesting; 530} 531 532static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, unsigned long cnt, int n) 533{ 534 l0 = sline[l0].p_lno[n]; 535 l1 = sline[l1].p_lno[n]; 536 printf(" -%lu,%lu", l0, l1-l0); 537} 538 539static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent) 540{ 541 unsigned long mark = (1UL<<num_parent); 542 int i; 543 unsigned long lno = 0; 544 545 while (1) { 546 struct sline *sl = &sline[lno]; 547 int hunk_end; 548 while (lno < cnt && !(sline[lno].flag & mark)) 549 lno++; 550 if (cnt <= lno) 551 break; 552 for (hunk_end = lno + 1; hunk_end < cnt; hunk_end++) 553 if (!(sline[hunk_end].flag & mark)) 554 break; 555 for (i = 0; i <= num_parent; i++) putchar(combine_marker); 556 for (i = 0; i < num_parent; i++) 557 show_parent_lno(sline, lno, hunk_end, cnt, i); 558 printf(" +%lu,%lu ", lno+1, hunk_end-lno); 559 for (i = 0; i <= num_parent; i++) putchar(combine_marker); 560 putchar('\n'); 561 while (lno < hunk_end) { 562 struct lline *ll; 563 int j; 564 unsigned long p_mask; 565 sl = &sline[lno++]; 566 ll = sl->lost_head; 567 while (ll) { 568 for (j = 0; j < num_parent; j++) { 569 if (ll->parent_map & (1UL<<j)) 570 putchar('-'); 571 else 572 putchar(' '); 573 } 574 puts(ll->line); 575 ll = ll->next; 576 } 577 p_mask = 1; 578 for (j = 0; j < num_parent; j++) { 579 if (p_mask & sl->flag) 580 putchar('+'); 581 else 582 putchar(' '); 583 p_mask <<= 1; 584 } 585 printf("%.*s\n", sl->len, sl->bol); 586 } 587 } 588} 589 590static void reuse_combine_diff(struct sline *sline, unsigned long cnt, 591 int i, int j) 592{ 593 /* We have already examined parent j and we know parent i 594 * and parent j are the same, so reuse the combined result 595 * of parent j for parent i. 596 */ 597 unsigned long lno, imask, jmask; 598 imask = (1UL<<i); 599 jmask = (1UL<<j); 600 601 for (lno = 0; lno < cnt; lno++) { 602 struct lline *ll = sline->lost_head; 603 sline->p_lno[i] = sline->p_lno[j]; 604 while (ll) { 605 if (ll->parent_map & jmask) 606 ll->parent_map |= imask; 607 ll = ll->next; 608 } 609 if (sline->flag & jmask) 610 sline->flag |= imask; 611 sline++; 612 } 613} 614 615int show_combined_diff(struct combine_diff_path *elem, int num_parent, 616 int dense, const char *header) 617{ 618 unsigned long size, cnt, lno; 619 char *result, *cp, *ep; 620 struct sline *sline; /* survived lines */ 621 int mode_differs = 0; 622 int i, show_hunks, shown_header = 0; 623 char ourtmp_buf[TMPPATHLEN]; 624 char *ourtmp = ourtmp_buf; 625 626 /* Read the result of merge first */ 627 if (memcmp(elem->sha1, null_sha1, 20)) { 628 result = grab_blob(elem->sha1, &size); 629 write_to_temp_file(ourtmp, result, size); 630 } 631 else { 632 /* Used by diff-tree to read from the working tree */ 633 struct stat st; 634 int fd; 635 ourtmp = elem->path; 636 if (0 <= (fd = open(ourtmp, O_RDONLY)) && 637 !fstat(fd, &st)) { 638 int len = st.st_size; 639 int cnt = 0; 640 641 size = len; 642 result = xmalloc(len + 1); 643 while (cnt < len) { 644 int done = xread(fd, result+cnt, len-cnt); 645 if (done == 0) 646 break; 647 if (done < 0) 648 die("read error '%s'", ourtmp); 649 cnt += done; 650 } 651 result[len] = 0; 652 } 653 else { 654 /* deleted file */ 655 size = 0; 656 result = xmalloc(1); 657 result[0] = 0; 658 ourtmp = "/dev/null"; 659 } 660 if (0 <= fd) 661 close(fd); 662 } 663 664 for (cnt = 0, cp = result; cp - result < size; cp++) { 665 if (*cp == '\n') 666 cnt++; 667 } 668 if (result[size-1] != '\n') 669 cnt++; /* incomplete line */ 670 671 sline = xcalloc(cnt+1, sizeof(*sline)); 672 ep = result; 673 sline[0].bol = result; 674 for (lno = 0, cp = result; cp - result < size; cp++) { 675 if (*cp == '\n') { 676 sline[lno].lost_tail = &sline[lno].lost_head; 677 sline[lno].len = cp - sline[lno].bol; 678 sline[lno].flag = 0; 679 lno++; 680 if (lno < cnt) 681 sline[lno].bol = cp + 1; 682 } 683 } 684 if (result[size-1] != '\n') { 685 sline[cnt-1].lost_tail = &sline[cnt-1].lost_head; 686 sline[cnt-1].len = size - (sline[cnt-1].bol - result); 687 sline[cnt-1].flag = 0; 688 } 689 690 sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long)); 691 for (lno = 0; lno < cnt; lno++) 692 sline[lno+1].p_lno = sline[lno].p_lno + num_parent; 693 694 for (i = 0; i < num_parent; i++) { 695 int j; 696 for (j = 0; j < i; j++) { 697 if (!memcmp(elem->parent[i].sha1, 698 elem->parent[j].sha1, 20)) { 699 reuse_combine_diff(sline, cnt, i, j); 700 break; 701 } 702 } 703 if (i <= j) 704 combine_diff(elem->parent[i].sha1, ourtmp, sline, 705 cnt, i, num_parent); 706 if (elem->parent[i].mode != elem->mode) 707 mode_differs = 1; 708 } 709 710 show_hunks = make_hunks(sline, cnt, num_parent, dense); 711 712 if (show_hunks || mode_differs) { 713 const char *abb; 714 char null_abb[DEFAULT_ABBREV + 1]; 715 716 memset(null_abb, '0', DEFAULT_ABBREV); 717 null_abb[DEFAULT_ABBREV] = 0; 718 if (header) { 719 shown_header++; 720 puts(header); 721 } 722 printf("diff --%s ", dense ? "cc" : "combined"); 723 if (quote_c_style(elem->path, NULL, NULL, 0)) 724 quote_c_style(elem->path, NULL, stdout, 0); 725 else 726 printf("%s", elem->path); 727 putchar('\n'); 728 printf("index "); 729 for (i = 0; i < num_parent; i++) { 730 if (elem->parent[i].mode != elem->mode) 731 mode_differs = 1; 732 if (memcmp(elem->parent[i].sha1, null_sha1, 20)) 733 abb = find_unique_abbrev(elem->parent[i].sha1, 734 DEFAULT_ABBREV); 735 else 736 abb = null_abb; 737 printf("%s%s", i ? "," : "", abb); 738 } 739 if (memcmp(elem->sha1, null_sha1, 20)) 740 abb = find_unique_abbrev(elem->sha1, DEFAULT_ABBREV); 741 else 742 abb = null_abb; 743 printf("..%s\n", abb); 744 745 if (mode_differs) { 746 printf("mode "); 747 for (i = 0; i < num_parent; i++) { 748 printf("%s%06o", i ? "," : "", 749 elem->parent[i].mode); 750 } 751 printf("..%06o\n", elem->mode); 752 } 753 /* if (show_hunks) perhaps */ 754 dump_sline(sline, cnt, num_parent); 755 } 756 if (ourtmp == ourtmp_buf) 757 unlink(ourtmp); 758 free(result); 759 760 for (i = 0; i < cnt; i++) { 761 if (sline[i].lost_head) { 762 struct lline *ll = sline[i].lost_head; 763 while (ll) { 764 struct lline *tmp = ll; 765 ll = ll->next; 766 free(tmp); 767 } 768 } 769 } 770 free(sline[0].p_lno); 771 free(sline); 772 return shown_header; 773} 774 775int diff_tree_combined_merge(const unsigned char *sha1, 776 const char *header, int dense) 777{ 778 struct commit *commit = lookup_commit(sha1); 779 struct diff_options diffopts; 780 struct commit_list *parents; 781 struct combine_diff_path *p, *paths = NULL; 782 int num_parent, i, num_paths; 783 784 diff_setup(&diffopts); 785 diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; 786 diffopts.recursive = 1; 787 788 /* count parents */ 789 for (parents = commit->parents, num_parent = 0; 790 parents; 791 parents = parents->next, num_parent++) 792 ; /* nothing */ 793 794 /* find set of paths that everybody touches */ 795 for (parents = commit->parents, i = 0; 796 parents; 797 parents = parents->next, i++) { 798 struct commit *parent = parents->item; 799 diff_tree_sha1(parent->object.sha1, commit->object.sha1, "", 800 &diffopts); 801 paths = intersect_paths(paths, i, num_parent); 802 diff_flush(&diffopts); 803 } 804 805 /* find out surviving paths */ 806 for (num_paths = 0, p = paths; p; p = p->next) { 807 if (p->len) 808 num_paths++; 809 } 810 if (num_paths) { 811 for (p = paths; p; p = p->next) { 812 if (!p->len) 813 continue; 814 if (show_combined_diff(p, num_parent, dense, header)) 815 header = NULL; 816 } 817 } 818 819 /* Clean things up */ 820 while (paths) { 821 struct combine_diff_path *tmp = paths; 822 paths = paths->next; 823 free(tmp); 824 } 825 return 0; 826}