Git fork
at reftables-rust 1672 lines 44 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2#define DISABLE_SIGN_COMPARE_WARNINGS 3 4#include "git-compat-util.h" 5#include "odb.h" 6#include "commit.h" 7#include "convert.h" 8#include "diff.h" 9#include "diffcore.h" 10#include "environment.h" 11#include "hex.h" 12#include "object-name.h" 13#include "quote.h" 14#include "xdiff-interface.h" 15#include "xdiff/xmacros.h" 16#include "log-tree.h" 17#include "refs.h" 18#include "tree.h" 19#include "userdiff.h" 20#include "oid-array.h" 21#include "revision.h" 22 23static int compare_paths(const struct combine_diff_path *one, 24 const struct diff_filespec *two) 25{ 26 if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode)) 27 return strcmp(one->path, two->path); 28 29 return base_name_compare(one->path, strlen(one->path), one->mode, 30 two->path, strlen(two->path), two->mode); 31} 32 33static int filename_changed(char status) 34{ 35 return status == 'R' || status == 'C'; 36} 37 38static struct combine_diff_path *intersect_paths( 39 struct combine_diff_path *curr, 40 int n, 41 int num_parent, 42 int combined_all_paths) 43{ 44 struct diff_queue_struct *q = &diff_queued_diff; 45 struct combine_diff_path *p, **tail = &curr; 46 int i, j, cmp; 47 48 if (!n) { 49 for (i = 0; i < q->nr; i++) { 50 if (diff_unmodified_pair(q->queue[i])) 51 continue; 52 p = combine_diff_path_new(q->queue[i]->two->path, 53 strlen(q->queue[i]->two->path), 54 q->queue[i]->two->mode, 55 &q->queue[i]->two->oid, 56 num_parent); 57 oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid); 58 p->parent[n].mode = q->queue[i]->one->mode; 59 p->parent[n].status = q->queue[i]->status; 60 61 if (combined_all_paths && 62 filename_changed(p->parent[n].status)) { 63 p->parent[n].path = xstrdup(q->queue[i]->one->path); 64 } 65 *tail = p; 66 tail = &p->next; 67 } 68 return curr; 69 } 70 71 /* 72 * paths in curr (linked list) and q->queue[] (array) are 73 * both sorted in the tree order. 74 */ 75 i = 0; 76 while ((p = *tail) != NULL) { 77 cmp = ((i >= q->nr) 78 ? -1 : compare_paths(p, q->queue[i]->two)); 79 80 if (cmp < 0) { 81 /* p->path not in q->queue[]; drop it */ 82 *tail = p->next; 83 for (j = 0; j < num_parent; j++) 84 free(p->parent[j].path); 85 free(p); 86 continue; 87 } 88 89 if (cmp > 0) { 90 /* q->queue[i] not in p->path; skip it */ 91 i++; 92 continue; 93 } 94 95 oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid); 96 p->parent[n].mode = q->queue[i]->one->mode; 97 p->parent[n].status = q->queue[i]->status; 98 if (combined_all_paths && 99 filename_changed(p->parent[n].status)) 100 p->parent[n].path = xstrdup(q->queue[i]->one->path); 101 102 tail = &p->next; 103 i++; 104 } 105 return curr; 106} 107 108/* Lines lost from parent */ 109struct lline { 110 struct lline *next, *prev; 111 int len; 112 unsigned long parent_map; 113 char line[FLEX_ARRAY]; 114}; 115 116/* Lines lost from current parent (before coalescing) */ 117struct plost { 118 struct lline *lost_head, *lost_tail; 119 int len; 120}; 121 122/* Lines surviving in the merge result */ 123struct sline { 124 /* Accumulated and coalesced lost lines */ 125 struct lline *lost; 126 int lenlost; 127 struct plost plost; 128 char *bol; 129 int len; 130 /* bit 0 up to (N-1) are on if the parent has this line (i.e. 131 * we did not change it). 132 * bit N is used for "interesting" lines, including context. 133 * bit (N+1) is used for "do not show deletion before this". 134 */ 135 unsigned long flag; 136 unsigned long *p_lno; 137}; 138 139static int match_string_spaces(const char *line1, int len1, 140 const char *line2, int len2, 141 long flags) 142{ 143 if (flags & XDF_WHITESPACE_FLAGS) { 144 for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--); 145 for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--); 146 } 147 148 if (!(flags & (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE))) 149 return (len1 == len2 && !memcmp(line1, line2, len1)); 150 151 while (len1 > 0 && len2 > 0) { 152 len1--; 153 len2--; 154 if (XDL_ISSPACE(line1[len1]) || XDL_ISSPACE(line2[len2])) { 155 if ((flags & XDF_IGNORE_WHITESPACE_CHANGE) && 156 (!XDL_ISSPACE(line1[len1]) || !XDL_ISSPACE(line2[len2]))) 157 return 0; 158 159 for (; len1 > 0 && XDL_ISSPACE(line1[len1]); len1--); 160 for (; len2 > 0 && XDL_ISSPACE(line2[len2]); len2--); 161 } 162 if (line1[len1] != line2[len2]) 163 return 0; 164 } 165 166 if (flags & XDF_IGNORE_WHITESPACE) { 167 /* Consume remaining spaces */ 168 for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--); 169 for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--); 170 } 171 172 /* We matched full line1 and line2 */ 173 if (!len1 && !len2) 174 return 1; 175 176 return 0; 177} 178 179enum coalesce_direction { MATCH, BASE, NEW }; 180 181/* Coalesce new lines into base by finding LCS */ 182static struct lline *coalesce_lines(struct lline *base, int *lenbase, 183 struct lline *newline, int lennew, 184 unsigned long parent, long flags) 185{ 186 int **lcs; 187 enum coalesce_direction **directions; 188 struct lline *baseend, *newend = NULL; 189 int i, j, origbaselen = *lenbase; 190 191 if (!newline) 192 return base; 193 194 if (!base) { 195 *lenbase = lennew; 196 return newline; 197 } 198 199 /* 200 * Coalesce new lines into base by finding the LCS 201 * - Create the table to run dynamic programming 202 * - Compute the LCS 203 * - Then reverse read the direction structure: 204 * - If we have MATCH, assign parent to base flag, and consume 205 * both baseend and newend 206 * - Else if we have BASE, consume baseend 207 * - Else if we have NEW, insert newend lline into base and 208 * consume newend 209 */ 210 CALLOC_ARRAY(lcs, st_add(origbaselen, 1)); 211 CALLOC_ARRAY(directions, st_add(origbaselen, 1)); 212 for (i = 0; i < origbaselen + 1; i++) { 213 CALLOC_ARRAY(lcs[i], st_add(lennew, 1)); 214 CALLOC_ARRAY(directions[i], st_add(lennew, 1)); 215 directions[i][0] = BASE; 216 } 217 for (j = 1; j < lennew + 1; j++) 218 directions[0][j] = NEW; 219 220 for (i = 1, baseend = base; i < origbaselen + 1; i++) { 221 for (j = 1, newend = newline; j < lennew + 1; j++) { 222 if (match_string_spaces(baseend->line, baseend->len, 223 newend->line, newend->len, flags)) { 224 lcs[i][j] = lcs[i - 1][j - 1] + 1; 225 directions[i][j] = MATCH; 226 } else if (lcs[i][j - 1] >= lcs[i - 1][j]) { 227 lcs[i][j] = lcs[i][j - 1]; 228 directions[i][j] = NEW; 229 } else { 230 lcs[i][j] = lcs[i - 1][j]; 231 directions[i][j] = BASE; 232 } 233 if (newend->next) 234 newend = newend->next; 235 } 236 if (baseend->next) 237 baseend = baseend->next; 238 } 239 240 for (i = 0; i < origbaselen + 1; i++) 241 free(lcs[i]); 242 free(lcs); 243 244 /* At this point, baseend and newend point to the end of each lists */ 245 i--; 246 j--; 247 while (i != 0 || j != 0) { 248 if (directions[i][j] == MATCH) { 249 baseend->parent_map |= 1<<parent; 250 baseend = baseend->prev; 251 newend = newend->prev; 252 i--; 253 j--; 254 } else if (directions[i][j] == NEW) { 255 struct lline *lline; 256 257 lline = newend; 258 /* Remove lline from new list and update newend */ 259 if (lline->prev) 260 lline->prev->next = lline->next; 261 else 262 newline = lline->next; 263 if (lline->next) 264 lline->next->prev = lline->prev; 265 266 newend = lline->prev; 267 j--; 268 269 /* Add lline to base list */ 270 if (baseend) { 271 lline->next = baseend->next; 272 lline->prev = baseend; 273 if (lline->prev) 274 lline->prev->next = lline; 275 } 276 else { 277 lline->next = base; 278 base = lline; 279 } 280 (*lenbase)++; 281 282 if (lline->next) 283 lline->next->prev = lline; 284 285 } else { 286 baseend = baseend->prev; 287 i--; 288 } 289 } 290 291 newend = newline; 292 while (newend) { 293 struct lline *lline = newend; 294 newend = newend->next; 295 free(lline); 296 } 297 298 for (i = 0; i < origbaselen + 1; i++) 299 free(directions[i]); 300 free(directions); 301 302 return base; 303} 304 305static char *grab_blob(struct repository *r, 306 const struct object_id *oid, unsigned int mode, 307 unsigned long *size, struct userdiff_driver *textconv, 308 const char *path) 309{ 310 char *blob; 311 enum object_type type; 312 313 if (S_ISGITLINK(mode)) { 314 struct strbuf buf = STRBUF_INIT; 315 strbuf_addf(&buf, "Subproject commit %s\n", oid_to_hex(oid)); 316 *size = buf.len; 317 blob = strbuf_detach(&buf, NULL); 318 } else if (is_null_oid(oid)) { 319 /* deleted blob */ 320 *size = 0; 321 return xcalloc(1, 1); 322 } else if (textconv) { 323 struct diff_filespec *df = alloc_filespec(path); 324 fill_filespec(df, oid, 1, mode); 325 *size = fill_textconv(r, textconv, df, &blob); 326 free_filespec(df); 327 } else { 328 blob = odb_read_object(r->objects, oid, &type, size); 329 if (!blob) 330 die(_("unable to read %s"), oid_to_hex(oid)); 331 if (type != OBJ_BLOB) 332 die("object '%s' is not a blob!", oid_to_hex(oid)); 333 } 334 return blob; 335} 336 337static void append_lost(struct sline *sline, int n, const char *line, int len) 338{ 339 struct lline *lline; 340 unsigned long this_mask = (1UL<<n); 341 if (line[len-1] == '\n') 342 len--; 343 344 FLEX_ALLOC_MEM(lline, line, line, len); 345 lline->len = len; 346 lline->next = NULL; 347 lline->prev = sline->plost.lost_tail; 348 if (lline->prev) 349 lline->prev->next = lline; 350 else 351 sline->plost.lost_head = lline; 352 sline->plost.lost_tail = lline; 353 sline->plost.len++; 354 lline->parent_map = this_mask; 355} 356 357struct combine_diff_state { 358 unsigned int lno; 359 int ob, on, nb, nn; 360 unsigned long nmask; 361 int num_parent; 362 int n; 363 struct sline *sline; 364 struct sline *lost_bucket; 365}; 366 367static void consume_hunk(void *state_, 368 long ob, long on, 369 long nb, long nn, 370 const char *func UNUSED, long funclen UNUSED) 371{ 372 struct combine_diff_state *state = state_; 373 374 state->ob = ob; 375 state->on = on; 376 state->nb = nb; 377 state->nn = nn; 378 state->lno = state->nb; 379 if (state->nn == 0) { 380 /* @@ -X,Y +N,0 @@ removed Y lines 381 * that would have come *after* line N 382 * in the result. Our lost buckets hang 383 * to the line after the removed lines, 384 * 385 * Note that this is correct even when N == 0, 386 * in which case the hunk removes the first 387 * line in the file. 388 */ 389 state->lost_bucket = &state->sline[state->nb]; 390 if (!state->nb) 391 state->nb = 1; 392 } else { 393 state->lost_bucket = &state->sline[state->nb-1]; 394 } 395 if (!state->sline[state->nb-1].p_lno) 396 CALLOC_ARRAY(state->sline[state->nb - 1].p_lno, 397 state->num_parent); 398 state->sline[state->nb-1].p_lno[state->n] = state->ob; 399} 400 401static int consume_line(void *state_, char *line, unsigned long len) 402{ 403 struct combine_diff_state *state = state_; 404 if (!state->lost_bucket) 405 return 0; /* not in any hunk yet */ 406 switch (line[0]) { 407 case '-': 408 append_lost(state->lost_bucket, state->n, line+1, len-1); 409 break; 410 case '+': 411 state->sline[state->lno-1].flag |= state->nmask; 412 state->lno++; 413 break; 414 } 415 return 0; 416} 417 418static void combine_diff(struct repository *r, 419 const struct object_id *parent, unsigned int mode, 420 mmfile_t *result_file, 421 struct sline *sline, unsigned int cnt, int n, 422 int num_parent, int result_deleted, 423 struct userdiff_driver *textconv, 424 const char *path, long flags) 425{ 426 unsigned int p_lno, lno; 427 unsigned long nmask = (1UL << n); 428 xpparam_t xpp; 429 xdemitconf_t xecfg; 430 mmfile_t parent_file; 431 struct combine_diff_state state; 432 unsigned long sz; 433 434 if (result_deleted) 435 return; /* result deleted */ 436 437 parent_file.ptr = grab_blob(r, parent, mode, &sz, textconv, path); 438 parent_file.size = sz; 439 memset(&xpp, 0, sizeof(xpp)); 440 xpp.flags = flags; 441 memset(&xecfg, 0, sizeof(xecfg)); 442 memset(&state, 0, sizeof(state)); 443 state.nmask = nmask; 444 state.sline = sline; 445 state.lno = 1; 446 state.num_parent = num_parent; 447 state.n = n; 448 449 if (xdi_diff_outf(&parent_file, result_file, consume_hunk, 450 consume_line, &state, &xpp, &xecfg)) 451 die("unable to generate combined diff for %s", 452 oid_to_hex(parent)); 453 free(parent_file.ptr); 454 455 /* Assign line numbers for this parent. 456 * 457 * sline[lno].p_lno[n] records the first line number 458 * (counting from 1) for parent N if the final hunk display 459 * started by showing sline[lno] (possibly showing the lost 460 * lines attached to it first). 461 */ 462 for (lno = 0, p_lno = 1; lno <= cnt; lno++) { 463 struct lline *ll; 464 sline[lno].p_lno[n] = p_lno; 465 466 /* Coalesce new lines */ 467 if (sline[lno].plost.lost_head) { 468 struct sline *sl = &sline[lno]; 469 sl->lost = coalesce_lines(sl->lost, &sl->lenlost, 470 sl->plost.lost_head, 471 sl->plost.len, n, flags); 472 sl->plost.lost_head = sl->plost.lost_tail = NULL; 473 sl->plost.len = 0; 474 } 475 476 /* How many lines would this sline advance the p_lno? */ 477 ll = sline[lno].lost; 478 while (ll) { 479 if (ll->parent_map & nmask) 480 p_lno++; /* '-' means parent had it */ 481 ll = ll->next; 482 } 483 if (lno < cnt && !(sline[lno].flag & nmask)) 484 p_lno++; /* no '+' means parent had it */ 485 } 486 sline[lno].p_lno[n] = p_lno; /* trailer */ 487} 488 489static unsigned long context = 3; 490static char combine_marker = '@'; 491 492static int interesting(struct sline *sline, unsigned long all_mask) 493{ 494 /* If some parents lost lines here, or if we have added to 495 * some parent, it is interesting. 496 */ 497 return ((sline->flag & all_mask) || sline->lost); 498} 499 500static unsigned long adjust_hunk_tail(struct sline *sline, 501 unsigned long all_mask, 502 unsigned long hunk_begin, 503 unsigned long i) 504{ 505 /* i points at the first uninteresting line. If the last line 506 * of the hunk was interesting only because it has some 507 * deletion, then it is not all that interesting for the 508 * purpose of giving trailing context lines. This is because 509 * we output '-' line and then unmodified sline[i-1] itself in 510 * that case which gives us one extra context line. 511 */ 512 if ((hunk_begin + 1 <= i) && !(sline[i-1].flag & all_mask)) 513 i--; 514 return i; 515} 516 517static unsigned long find_next(struct sline *sline, 518 unsigned long mark, 519 unsigned long i, 520 unsigned long cnt, 521 int look_for_uninteresting) 522{ 523 /* We have examined up to i-1 and are about to look at i. 524 * Find next interesting or uninteresting line. Here, 525 * "interesting" does not mean interesting(), but marked by 526 * the give_context() function below (i.e. it includes context 527 * lines that are not interesting to interesting() function 528 * that are surrounded by interesting() ones. 529 */ 530 while (i <= cnt) 531 if (look_for_uninteresting 532 ? !(sline[i].flag & mark) 533 : (sline[i].flag & mark)) 534 return i; 535 else 536 i++; 537 return i; 538} 539 540static int give_context(struct sline *sline, unsigned long cnt, int num_parent) 541{ 542 unsigned long all_mask = (1UL<<num_parent) - 1; 543 unsigned long mark = (1UL<<num_parent); 544 unsigned long no_pre_delete = (2UL<<num_parent); 545 unsigned long i; 546 547 /* Two groups of interesting lines may have a short gap of 548 * uninteresting lines. Connect such groups to give them a 549 * bit of context. 550 * 551 * We first start from what the interesting() function says, 552 * and mark them with "mark", and paint context lines with the 553 * mark. So interesting() would still say false for such context 554 * lines but they are treated as "interesting" in the end. 555 */ 556 i = find_next(sline, mark, 0, cnt, 0); 557 if (cnt < i) 558 return 0; 559 560 while (i <= cnt) { 561 unsigned long j = (context < i) ? (i - context) : 0; 562 unsigned long k; 563 564 /* Paint a few lines before the first interesting line. */ 565 while (j < i) { 566 if (!(sline[j].flag & mark)) 567 sline[j].flag |= no_pre_delete; 568 sline[j++].flag |= mark; 569 } 570 571 again: 572 /* we know up to i is to be included. where does the 573 * next uninteresting one start? 574 */ 575 j = find_next(sline, mark, i, cnt, 1); 576 if (cnt < j) 577 break; /* the rest are all interesting */ 578 579 /* lookahead context lines */ 580 k = find_next(sline, mark, j, cnt, 0); 581 j = adjust_hunk_tail(sline, all_mask, i, j); 582 583 if (k < j + context) { 584 /* k is interesting and [j,k) are not, but 585 * paint them interesting because the gap is small. 586 */ 587 while (j < k) 588 sline[j++].flag |= mark; 589 i = k; 590 goto again; 591 } 592 593 /* j is the first uninteresting line and there is 594 * no overlap beyond it within context lines. Paint 595 * the trailing edge a bit. 596 */ 597 i = k; 598 k = (j + context < cnt+1) ? j + context : cnt+1; 599 while (j < k) 600 sline[j++].flag |= mark; 601 } 602 return 1; 603} 604 605static int make_hunks(struct sline *sline, unsigned long cnt, 606 int num_parent, int dense) 607{ 608 unsigned long all_mask = (1UL<<num_parent) - 1; 609 unsigned long mark = (1UL<<num_parent); 610 unsigned long i; 611 int has_interesting = 0; 612 613 for (i = 0; i <= cnt; i++) { 614 if (interesting(&sline[i], all_mask)) 615 sline[i].flag |= mark; 616 else 617 sline[i].flag &= ~mark; 618 } 619 if (!dense) 620 return give_context(sline, cnt, num_parent); 621 622 /* Look at each hunk, and if we have changes from only one 623 * parent, or the changes are the same from all but one 624 * parent, mark that uninteresting. 625 */ 626 i = 0; 627 while (i <= cnt) { 628 unsigned long j, hunk_begin, hunk_end; 629 unsigned long same_diff; 630 while (i <= cnt && !(sline[i].flag & mark)) 631 i++; 632 if (cnt < i) 633 break; /* No more interesting hunks */ 634 hunk_begin = i; 635 for (j = i + 1; j <= cnt; j++) { 636 if (!(sline[j].flag & mark)) { 637 /* Look beyond the end to see if there 638 * is an interesting line after this 639 * hunk within context span. 640 */ 641 unsigned long la; /* lookahead */ 642 int contin = 0; 643 la = adjust_hunk_tail(sline, all_mask, 644 hunk_begin, j); 645 la = (la + context < cnt + 1) ? 646 (la + context) : cnt + 1; 647 while (la && j <= --la) { 648 if (sline[la].flag & mark) { 649 contin = 1; 650 break; 651 } 652 } 653 if (!contin) 654 break; 655 j = la; 656 } 657 } 658 hunk_end = j; 659 660 /* [i..hunk_end) are interesting. Now is it really 661 * interesting? We check if there are only two versions 662 * and the result matches one of them. That is, we look 663 * at: 664 * (+) line, which records lines added to which parents; 665 * this line appears in the result. 666 * (-) line, which records from what parents the line 667 * was removed; this line does not appear in the result. 668 * then check the set of parents the result has difference 669 * from, from all lines. If there are lines that has 670 * different set of parents that the result has differences 671 * from, that means we have more than two versions. 672 * 673 * Even when we have only two versions, if the result does 674 * not match any of the parents, the it should be considered 675 * interesting. In such a case, we would have all '+' line. 676 * After passing the above "two versions" test, that would 677 * appear as "the same set of parents" to be "all parents". 678 */ 679 same_diff = 0; 680 has_interesting = 0; 681 for (j = i; j < hunk_end && !has_interesting; j++) { 682 unsigned long this_diff = sline[j].flag & all_mask; 683 struct lline *ll = sline[j].lost; 684 if (this_diff) { 685 /* This has some changes. Is it the 686 * same as others? 687 */ 688 if (!same_diff) 689 same_diff = this_diff; 690 else if (same_diff != this_diff) { 691 has_interesting = 1; 692 break; 693 } 694 } 695 while (ll && !has_interesting) { 696 /* Lost this line from these parents; 697 * who are they? Are they the same? 698 */ 699 this_diff = ll->parent_map; 700 if (!same_diff) 701 same_diff = this_diff; 702 else if (same_diff != this_diff) { 703 has_interesting = 1; 704 } 705 ll = ll->next; 706 } 707 } 708 709 if (!has_interesting && same_diff != all_mask) { 710 /* This hunk is not that interesting after all */ 711 for (j = hunk_begin; j < hunk_end; j++) 712 sline[j].flag &= ~mark; 713 } 714 i = hunk_end; 715 } 716 717 has_interesting = give_context(sline, cnt, num_parent); 718 return has_interesting; 719} 720 721static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n, unsigned long null_context) 722{ 723 l0 = sline[l0].p_lno[n]; 724 l1 = sline[l1].p_lno[n]; 725 printf(" -%lu,%lu", l0, l1-l0-null_context); 726} 727 728static int hunk_comment_line(const char *bol) 729{ 730 int ch; 731 732 if (!bol) 733 return 0; 734 ch = *bol & 0xff; 735 return (isalpha(ch) || ch == '_' || ch == '$'); 736} 737 738static void show_line_to_eol(const char *line, int len, const char *reset) 739{ 740 int saw_cr_at_eol = 0; 741 if (len < 0) 742 len = strlen(line); 743 saw_cr_at_eol = (len && line[len-1] == '\r'); 744 745 printf("%.*s%s%s\n", len - saw_cr_at_eol, line, 746 reset, 747 saw_cr_at_eol ? "\r" : ""); 748} 749 750static void dump_sline(struct sline *sline, const char *line_prefix, 751 unsigned long cnt, int num_parent, 752 enum git_colorbool use_color, int result_deleted) 753{ 754 unsigned long mark = (1UL<<num_parent); 755 unsigned long no_pre_delete = (2UL<<num_parent); 756 int i; 757 unsigned long lno = 0; 758 const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO); 759 const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO); 760 const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW); 761 const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD); 762 const char *c_context = diff_get_color(use_color, DIFF_CONTEXT); 763 const char *c_reset = diff_get_color(use_color, DIFF_RESET); 764 765 if (result_deleted) 766 return; /* result deleted */ 767 768 while (1) { 769 unsigned long hunk_end; 770 unsigned long rlines; 771 const char *hunk_comment = NULL; 772 unsigned long null_context = 0; 773 774 while (lno <= cnt && !(sline[lno].flag & mark)) { 775 if (hunk_comment_line(sline[lno].bol)) 776 hunk_comment = sline[lno].bol; 777 lno++; 778 } 779 if (cnt < lno) 780 break; 781 else { 782 for (hunk_end = lno + 1; hunk_end <= cnt; hunk_end++) 783 if (!(sline[hunk_end].flag & mark)) 784 break; 785 } 786 rlines = hunk_end - lno; 787 if (cnt < hunk_end) 788 rlines--; /* pointing at the last delete hunk */ 789 790 if (!context) { 791 /* 792 * Even when running with --unified=0, all 793 * lines in the hunk needs to be processed in 794 * the loop below in order to show the 795 * deletion recorded in lost_head. However, 796 * we do not want to show the resulting line 797 * with all blank context markers in such a 798 * case. Compensate. 799 */ 800 unsigned long j; 801 for (j = lno; j < hunk_end; j++) 802 if (!(sline[j].flag & (mark-1))) 803 null_context++; 804 rlines -= null_context; 805 } 806 807 printf("%s%s", line_prefix, c_frag); 808 for (i = 0; i <= num_parent; i++) putchar(combine_marker); 809 for (i = 0; i < num_parent; i++) 810 show_parent_lno(sline, lno, hunk_end, i, null_context); 811 printf(" +%lu,%lu ", lno+1, rlines); 812 for (i = 0; i <= num_parent; i++) putchar(combine_marker); 813 814 if (hunk_comment) { 815 int comment_end = 0; 816 for (i = 0; i < 40; i++) { 817 int ch = hunk_comment[i] & 0xff; 818 if (!ch || ch == '\n') 819 break; 820 if (!isspace(ch)) 821 comment_end = i; 822 } 823 if (comment_end) 824 printf("%s%s %s%s", c_reset, 825 c_context, c_reset, 826 c_func); 827 for (i = 0; i < comment_end; i++) 828 putchar(hunk_comment[i]); 829 } 830 831 printf("%s\n", c_reset); 832 while (lno < hunk_end) { 833 struct lline *ll; 834 int j; 835 unsigned long p_mask; 836 struct sline *sl = &sline[lno++]; 837 ll = (sl->flag & no_pre_delete) ? NULL : sl->lost; 838 while (ll) { 839 printf("%s%s", line_prefix, c_old); 840 for (j = 0; j < num_parent; j++) { 841 if (ll->parent_map & (1UL<<j)) 842 putchar('-'); 843 else 844 putchar(' '); 845 } 846 show_line_to_eol(ll->line, -1, c_reset); 847 ll = ll->next; 848 } 849 if (cnt < lno) 850 break; 851 p_mask = 1; 852 fputs(line_prefix, stdout); 853 if (!(sl->flag & (mark-1))) { 854 /* 855 * This sline was here to hang the 856 * lost lines in front of it. 857 */ 858 if (!context) 859 continue; 860 fputs(c_context, stdout); 861 } 862 else 863 fputs(c_new, stdout); 864 for (j = 0; j < num_parent; j++) { 865 if (p_mask & sl->flag) 866 putchar('+'); 867 else 868 putchar(' '); 869 p_mask <<= 1; 870 } 871 show_line_to_eol(sl->bol, sl->len, c_reset); 872 } 873 } 874} 875 876static void reuse_combine_diff(struct sline *sline, unsigned long cnt, 877 int i, int j) 878{ 879 /* We have already examined parent j and we know parent i 880 * and parent j are the same, so reuse the combined result 881 * of parent j for parent i. 882 */ 883 unsigned long lno, imask, jmask; 884 imask = (1UL<<i); 885 jmask = (1UL<<j); 886 887 for (lno = 0; lno <= cnt; lno++) { 888 struct lline *ll = sline->lost; 889 sline->p_lno[i] = sline->p_lno[j]; 890 while (ll) { 891 if (ll->parent_map & jmask) 892 ll->parent_map |= imask; 893 ll = ll->next; 894 } 895 if (sline->flag & jmask) 896 sline->flag |= imask; 897 sline++; 898 } 899 /* the overall size of the file (sline[cnt]) */ 900 sline->p_lno[i] = sline->p_lno[j]; 901} 902 903static void dump_quoted_path(const char *head, 904 const char *prefix, 905 const char *path, 906 const char *line_prefix, 907 const char *c_meta, const char *c_reset) 908{ 909 static struct strbuf buf = STRBUF_INIT; 910 911 strbuf_reset(&buf); 912 strbuf_addstr(&buf, line_prefix); 913 strbuf_addstr(&buf, c_meta); 914 strbuf_addstr(&buf, head); 915 quote_two_c_style(&buf, prefix, path, 0); 916 strbuf_addstr(&buf, c_reset); 917 puts(buf.buf); 918} 919 920static void show_combined_header(struct combine_diff_path *elem, 921 int num_parent, 922 struct rev_info *rev, 923 const char *line_prefix, 924 int mode_differs, 925 int show_file_header) 926{ 927 struct diff_options *opt = &rev->diffopt; 928 int abbrev = opt->flags.full_index ? the_hash_algo->hexsz : DEFAULT_ABBREV; 929 const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/"; 930 const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/"; 931 const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO); 932 const char *c_reset = diff_get_color_opt(opt, DIFF_RESET); 933 const char *abb; 934 int added = 0; 935 int deleted = 0; 936 int i; 937 int dense = rev->dense_combined_merges; 938 939 if (rev->loginfo && !rev->no_commit_id) 940 show_log(rev); 941 942 dump_quoted_path(dense ? "diff --cc " : "diff --combined ", 943 "", elem->path, line_prefix, c_meta, c_reset); 944 printf("%s%sindex ", line_prefix, c_meta); 945 for (i = 0; i < num_parent; i++) { 946 abb = repo_find_unique_abbrev(the_repository, 947 &elem->parent[i].oid, abbrev); 948 printf("%s%s", i ? "," : "", abb); 949 } 950 abb = repo_find_unique_abbrev(the_repository, &elem->oid, abbrev); 951 printf("..%s%s\n", abb, c_reset); 952 953 if (mode_differs) { 954 deleted = !elem->mode; 955 956 /* We say it was added if nobody had it */ 957 added = !deleted; 958 for (i = 0; added && i < num_parent; i++) 959 if (elem->parent[i].status != 960 DIFF_STATUS_ADDED) 961 added = 0; 962 if (added) 963 printf("%s%snew file mode %06o", 964 line_prefix, c_meta, elem->mode); 965 else { 966 if (deleted) 967 printf("%s%sdeleted file ", 968 line_prefix, c_meta); 969 printf("mode "); 970 for (i = 0; i < num_parent; i++) { 971 printf("%s%06o", i ? "," : "", 972 elem->parent[i].mode); 973 } 974 if (elem->mode) 975 printf("..%06o", elem->mode); 976 } 977 printf("%s\n", c_reset); 978 } 979 980 if (!show_file_header) 981 return; 982 983 if (rev->combined_all_paths) { 984 for (i = 0; i < num_parent; i++) { 985 const char *path = elem->parent[i].path ? 986 elem->parent[i].path : 987 elem->path; 988 if (elem->parent[i].status == DIFF_STATUS_ADDED) 989 dump_quoted_path("--- ", "", "/dev/null", 990 line_prefix, c_meta, c_reset); 991 else 992 dump_quoted_path("--- ", a_prefix, path, 993 line_prefix, c_meta, c_reset); 994 } 995 } else { 996 if (added) 997 dump_quoted_path("--- ", "", "/dev/null", 998 line_prefix, c_meta, c_reset); 999 else 1000 dump_quoted_path("--- ", a_prefix, elem->path, 1001 line_prefix, c_meta, c_reset); 1002 } 1003 if (deleted) 1004 dump_quoted_path("+++ ", "", "/dev/null", 1005 line_prefix, c_meta, c_reset); 1006 else 1007 dump_quoted_path("+++ ", b_prefix, elem->path, 1008 line_prefix, c_meta, c_reset); 1009} 1010 1011static void show_patch_diff(struct combine_diff_path *elem, int num_parent, 1012 int working_tree_file, 1013 struct rev_info *rev) 1014{ 1015 struct diff_options *opt = &rev->diffopt; 1016 unsigned long result_size, cnt, lno; 1017 int result_deleted = 0; 1018 char *result, *cp; 1019 struct sline *sline; /* survived lines */ 1020 int mode_differs = 0; 1021 int i, show_hunks; 1022 mmfile_t result_file; 1023 struct userdiff_driver *userdiff; 1024 struct userdiff_driver *textconv = NULL; 1025 int is_binary; 1026 const char *line_prefix = diff_line_prefix(opt); 1027 1028 context = opt->context; 1029 userdiff = userdiff_find_by_path(opt->repo->index, elem->path); 1030 if (!userdiff) 1031 userdiff = userdiff_find_by_name("default"); 1032 if (opt->flags.allow_textconv) 1033 textconv = userdiff_get_textconv(opt->repo, userdiff); 1034 1035 /* Read the result of merge first */ 1036 if (!working_tree_file) 1037 result = grab_blob(opt->repo, &elem->oid, elem->mode, &result_size, 1038 textconv, elem->path); 1039 else { 1040 /* Used by diff-tree to read from the working tree */ 1041 struct stat st; 1042 int fd = -1; 1043 1044 if (lstat(elem->path, &st) < 0) 1045 goto deleted_file; 1046 1047 if (S_ISLNK(st.st_mode)) { 1048 struct strbuf buf = STRBUF_INIT; 1049 1050 if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) { 1051 error_errno("readlink(%s)", elem->path); 1052 return; 1053 } 1054 result_size = buf.len; 1055 result = strbuf_detach(&buf, NULL); 1056 elem->mode = canon_mode(st.st_mode); 1057 } else if (S_ISDIR(st.st_mode)) { 1058 struct object_id oid; 1059 if (repo_resolve_gitlink_ref(the_repository, elem->path, 1060 "HEAD", &oid) < 0) 1061 result = grab_blob(opt->repo, &elem->oid, 1062 elem->mode, &result_size, 1063 NULL, NULL); 1064 else 1065 result = grab_blob(opt->repo, &oid, elem->mode, 1066 &result_size, NULL, NULL); 1067 } else if (textconv) { 1068 struct diff_filespec *df = alloc_filespec(elem->path); 1069 fill_filespec(df, null_oid(the_hash_algo), 0, st.st_mode); 1070 result_size = fill_textconv(opt->repo, textconv, df, &result); 1071 free_filespec(df); 1072 } else if (0 <= (fd = open(elem->path, O_RDONLY))) { 1073 size_t len = xsize_t(st.st_size); 1074 ssize_t done; 1075 int is_file, i; 1076 1077 elem->mode = canon_mode(st.st_mode); 1078 /* if symlinks don't work, assume symlink if all parents 1079 * are symlinks 1080 */ 1081 is_file = has_symlinks; 1082 for (i = 0; !is_file && i < num_parent; i++) 1083 is_file = !S_ISLNK(elem->parent[i].mode); 1084 if (!is_file) 1085 elem->mode = canon_mode(S_IFLNK); 1086 1087 result_size = len; 1088 result = xmallocz(len); 1089 1090 done = read_in_full(fd, result, len); 1091 if (done < 0) 1092 die_errno("read error '%s'", elem->path); 1093 else if (done < len) 1094 die("early EOF '%s'", elem->path); 1095 1096 /* If not a fake symlink, apply filters, e.g. autocrlf */ 1097 if (is_file) { 1098 struct strbuf buf = STRBUF_INIT; 1099 1100 if (convert_to_git(rev->diffopt.repo->index, 1101 elem->path, result, len, &buf, global_conv_flags_eol)) { 1102 free(result); 1103 result = strbuf_detach(&buf, &len); 1104 result_size = len; 1105 } 1106 } 1107 } 1108 else { 1109 deleted_file: 1110 result_deleted = 1; 1111 result_size = 0; 1112 elem->mode = 0; 1113 result = xcalloc(1, 1); 1114 } 1115 1116 if (0 <= fd) 1117 close(fd); 1118 } 1119 1120 for (i = 0; i < num_parent; i++) { 1121 if (elem->parent[i].mode != elem->mode) { 1122 mode_differs = 1; 1123 break; 1124 } 1125 } 1126 1127 if (textconv) 1128 is_binary = 0; 1129 else if (userdiff->binary != -1) 1130 is_binary = userdiff->binary; 1131 else { 1132 is_binary = buffer_is_binary(result, result_size); 1133 for (i = 0; !is_binary && i < num_parent; i++) { 1134 char *buf; 1135 unsigned long size; 1136 buf = grab_blob(opt->repo, 1137 &elem->parent[i].oid, 1138 elem->parent[i].mode, 1139 &size, NULL, NULL); 1140 if (buffer_is_binary(buf, size)) 1141 is_binary = 1; 1142 free(buf); 1143 } 1144 } 1145 if (is_binary) { 1146 show_combined_header(elem, num_parent, rev, 1147 line_prefix, mode_differs, 0); 1148 printf("Binary files differ\n"); 1149 free(result); 1150 return; 1151 } 1152 1153 for (cnt = 0, cp = result; cp < result + result_size; cp++) { 1154 if (*cp == '\n') 1155 cnt++; 1156 } 1157 if (result_size && result[result_size-1] != '\n') 1158 cnt++; /* incomplete line */ 1159 1160 CALLOC_ARRAY(sline, st_add(cnt, 2)); 1161 sline[0].bol = result; 1162 for (lno = 0, cp = result; cp < result + result_size; cp++) { 1163 if (*cp == '\n') { 1164 sline[lno].len = cp - sline[lno].bol; 1165 lno++; 1166 if (lno < cnt) 1167 sline[lno].bol = cp + 1; 1168 } 1169 } 1170 if (result_size && result[result_size-1] != '\n') 1171 sline[cnt-1].len = result_size - (sline[cnt-1].bol - result); 1172 1173 result_file.ptr = result; 1174 result_file.size = result_size; 1175 1176 /* 1177 * Even p_lno[cnt+1] is valid -- that is for the end line number 1178 * for deletion hunk at the end. 1179 */ 1180 CALLOC_ARRAY(sline[0].p_lno, st_mult(st_add(cnt, 2), num_parent)); 1181 for (lno = 0; lno <= cnt; lno++) 1182 sline[lno+1].p_lno = sline[lno].p_lno + num_parent; 1183 1184 for (i = 0; i < num_parent; i++) { 1185 int j; 1186 for (j = 0; j < i; j++) { 1187 if (oideq(&elem->parent[i].oid, 1188 &elem->parent[j].oid)) { 1189 reuse_combine_diff(sline, cnt, i, j); 1190 break; 1191 } 1192 } 1193 if (i <= j) 1194 combine_diff(opt->repo, 1195 &elem->parent[i].oid, 1196 elem->parent[i].mode, 1197 &result_file, sline, 1198 cnt, i, num_parent, result_deleted, 1199 textconv, elem->path, opt->xdl_opts); 1200 } 1201 1202 show_hunks = make_hunks(sline, cnt, num_parent, rev->dense_combined_merges); 1203 1204 if (show_hunks || mode_differs || working_tree_file) { 1205 show_combined_header(elem, num_parent, rev, 1206 line_prefix, mode_differs, 1); 1207 dump_sline(sline, line_prefix, cnt, num_parent, 1208 opt->use_color, result_deleted); 1209 } 1210 free(result); 1211 1212 for (lno = 0; lno < cnt + 2; lno++) { 1213 if (sline[lno].lost) { 1214 struct lline *ll = sline[lno].lost; 1215 while (ll) { 1216 struct lline *tmp = ll; 1217 ll = ll->next; 1218 free(tmp); 1219 } 1220 } 1221 } 1222 free(sline[0].p_lno); 1223 free(sline); 1224} 1225 1226static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev) 1227{ 1228 struct diff_options *opt = &rev->diffopt; 1229 int line_termination, inter_name_termination, i; 1230 const char *line_prefix = diff_line_prefix(opt); 1231 1232 line_termination = opt->line_termination; 1233 inter_name_termination = '\t'; 1234 if (!line_termination) 1235 inter_name_termination = 0; 1236 1237 if (rev->loginfo && !rev->no_commit_id) 1238 show_log(rev); 1239 1240 1241 if (opt->output_format & DIFF_FORMAT_RAW) { 1242 printf("%s", line_prefix); 1243 1244 /* As many colons as there are parents */ 1245 for (i = 0; i < num_parent; i++) 1246 putchar(':'); 1247 1248 /* Show the modes */ 1249 for (i = 0; i < num_parent; i++) 1250 printf("%06o ", p->parent[i].mode); 1251 printf("%06o", p->mode); 1252 1253 /* Show sha1's */ 1254 for (i = 0; i < num_parent; i++) 1255 printf(" %s", diff_aligned_abbrev(&p->parent[i].oid, 1256 opt->abbrev)); 1257 printf(" %s ", diff_aligned_abbrev(&p->oid, opt->abbrev)); 1258 } 1259 1260 if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) { 1261 for (i = 0; i < num_parent; i++) 1262 putchar(p->parent[i].status); 1263 putchar(inter_name_termination); 1264 } 1265 1266 for (i = 0; i < num_parent; i++) 1267 if (rev->combined_all_paths) { 1268 const char *path = p->parent[i].path ? 1269 p->parent[i].path : 1270 p->path; 1271 write_name_quoted(path, stdout, inter_name_termination); 1272 } 1273 write_name_quoted(p->path, stdout, line_termination); 1274} 1275 1276/* 1277 * The result (p->elem) is from the working tree and their 1278 * parents are typically from multiple stages during a merge 1279 * (i.e. diff-files) or the state in HEAD and in the index 1280 * (i.e. diff-index). 1281 */ 1282void show_combined_diff(struct combine_diff_path *p, 1283 int num_parent, 1284 struct rev_info *rev) 1285{ 1286 struct diff_options *opt = &rev->diffopt; 1287 1288 if (opt->output_format & (DIFF_FORMAT_RAW | 1289 DIFF_FORMAT_NAME | 1290 DIFF_FORMAT_NAME_STATUS)) 1291 show_raw_diff(p, num_parent, rev); 1292 else if (opt->output_format & DIFF_FORMAT_PATCH) 1293 show_patch_diff(p, num_parent, 1, rev); 1294} 1295 1296static void free_combined_pair(struct diff_filepair *pair) 1297{ 1298 free(pair->two); 1299 free(pair); 1300} 1301 1302/* 1303 * A combine_diff_path expresses N parents on the LHS against 1 merge 1304 * result. Synthesize a diff_filepair that has N entries on the "one" 1305 * side and 1 entry on the "two" side. 1306 * 1307 * In the future, we might want to add more data to combine_diff_path 1308 * so that we can fill fields we are ignoring (most notably, size) here, 1309 * but currently nobody uses it, so this should suffice for now. 1310 */ 1311static struct diff_filepair *combined_pair(struct combine_diff_path *p, 1312 int num_parent) 1313{ 1314 int i; 1315 struct diff_filepair *pair; 1316 struct diff_filespec *pool; 1317 1318 CALLOC_ARRAY(pair, 1); 1319 CALLOC_ARRAY(pool, st_add(num_parent, 1)); 1320 pair->one = pool + 1; 1321 pair->two = pool; 1322 1323 for (i = 0; i < num_parent; i++) { 1324 pair->one[i].path = p->path; 1325 pair->one[i].mode = p->parent[i].mode; 1326 oidcpy(&pair->one[i].oid, &p->parent[i].oid); 1327 pair->one[i].oid_valid = !is_null_oid(&p->parent[i].oid); 1328 pair->one[i].has_more_entries = 1; 1329 } 1330 pair->one[num_parent - 1].has_more_entries = 0; 1331 1332 pair->two->path = p->path; 1333 pair->two->mode = p->mode; 1334 oidcpy(&pair->two->oid, &p->oid); 1335 pair->two->oid_valid = !is_null_oid(&p->oid); 1336 return pair; 1337} 1338 1339static void handle_combined_callback(struct diff_options *opt, 1340 struct combine_diff_path *paths, 1341 int num_parent, 1342 int num_paths) 1343{ 1344 struct combine_diff_path *p; 1345 struct diff_queue_struct q; 1346 int i; 1347 1348 CALLOC_ARRAY(q.queue, num_paths); 1349 q.alloc = num_paths; 1350 q.nr = num_paths; 1351 for (i = 0, p = paths; p; p = p->next) 1352 q.queue[i++] = combined_pair(p, num_parent); 1353 opt->format_callback(&q, opt, opt->format_callback_data); 1354 for (i = 0; i < num_paths; i++) 1355 free_combined_pair(q.queue[i]); 1356 free(q.queue); 1357} 1358 1359static const char *path_path(void *obj) 1360{ 1361 struct combine_diff_path *path = (struct combine_diff_path *)obj; 1362 1363 return path->path; 1364} 1365 1366/* 1367 * Diff stat formats which we always compute solely against the first parent. 1368 */ 1369#define STAT_FORMAT_MASK (DIFF_FORMAT_NUMSTAT \ 1370 | DIFF_FORMAT_SHORTSTAT \ 1371 | DIFF_FORMAT_SUMMARY \ 1372 | DIFF_FORMAT_DIRSTAT \ 1373 | DIFF_FORMAT_DIFFSTAT) 1374 1375/* find set of paths that every parent touches */ 1376static struct combine_diff_path *find_paths_generic(const struct object_id *oid, 1377 const struct oid_array *parents, 1378 struct diff_options *opt, 1379 int combined_all_paths) 1380{ 1381 struct combine_diff_path *paths = NULL; 1382 int i, num_parent = parents->nr; 1383 int output_format = opt->output_format; 1384 char *orderfile = opt->orderfile; 1385 1386 opt->output_format = DIFF_FORMAT_NO_OUTPUT; 1387 /* tell diff_tree to emit paths in sorted (=tree) order */ 1388 opt->orderfile = NULL; 1389 1390 /* D(A,P1...Pn) = D(A,P1) ^ ... ^ D(A,Pn) (wrt paths) */ 1391 for (i = 0; i < num_parent; i++) { 1392 /* 1393 * show stat against the first parent even when doing 1394 * combined diff. 1395 */ 1396 int stat_opt = output_format & STAT_FORMAT_MASK; 1397 if (i == 0 && stat_opt) 1398 opt->output_format = stat_opt; 1399 else 1400 opt->output_format = DIFF_FORMAT_NO_OUTPUT; 1401 diff_tree_oid(&parents->oid[i], oid, "", opt); 1402 diffcore_std(opt); 1403 paths = intersect_paths(paths, i, num_parent, 1404 combined_all_paths); 1405 1406 /* if showing diff, show it in requested order */ 1407 if (opt->output_format != DIFF_FORMAT_NO_OUTPUT && 1408 orderfile) { 1409 diffcore_order(orderfile); 1410 } 1411 1412 diff_flush(opt); 1413 } 1414 1415 opt->output_format = output_format; 1416 opt->orderfile = orderfile; 1417 return paths; 1418} 1419 1420 1421/* 1422 * find set of paths that everybody touches, assuming diff is run without 1423 * rename/copy detection, etc, comparing all trees simultaneously (= faster). 1424 */ 1425static struct combine_diff_path *find_paths_multitree( 1426 const struct object_id *oid, const struct oid_array *parents, 1427 struct diff_options *opt) 1428{ 1429 int i, nparent = parents->nr; 1430 const struct object_id **parents_oid; 1431 struct combine_diff_path *paths; 1432 struct strbuf base; 1433 1434 ALLOC_ARRAY(parents_oid, nparent); 1435 for (i = 0; i < nparent; i++) 1436 parents_oid[i] = &parents->oid[i]; 1437 1438 strbuf_init(&base, PATH_MAX); 1439 paths = diff_tree_paths(oid, parents_oid, nparent, &base, opt); 1440 1441 strbuf_release(&base); 1442 free(parents_oid); 1443 return paths; 1444} 1445 1446static int match_objfind(struct combine_diff_path *path, 1447 int num_parent, 1448 const struct oidset *set) 1449{ 1450 int i; 1451 if (oidset_contains(set, &path->oid)) 1452 return 1; 1453 for (i = 0; i < num_parent; i++) { 1454 if (oidset_contains(set, &path->parent[i].oid)) 1455 return 1; 1456 } 1457 return 0; 1458} 1459 1460static struct combine_diff_path *combined_objfind(struct diff_options *opt, 1461 struct combine_diff_path *paths, 1462 int num_parent) 1463{ 1464 struct combine_diff_path *ret = NULL, **tail = &ret; 1465 struct combine_diff_path *p = paths; 1466 1467 while (p) { 1468 struct combine_diff_path *next = p->next; 1469 1470 if (match_objfind(p, num_parent, opt->objfind)) { 1471 p->next = NULL; 1472 *tail = p; 1473 tail = &p->next; 1474 } else { 1475 free(p); 1476 } 1477 p = next; 1478 } 1479 1480 return ret; 1481} 1482 1483void diff_tree_combined(const struct object_id *oid, 1484 const struct oid_array *parents, 1485 struct rev_info *rev) 1486{ 1487 struct diff_options *opt = &rev->diffopt; 1488 struct diff_options diffopts; 1489 struct combine_diff_path *p, *paths; 1490 int i, num_paths, needsep, show_log_first, num_parent = parents->nr; 1491 int need_generic_pathscan; 1492 1493 if (opt->ignore_regex_nr) 1494 die("combined diff and '%s' cannot be used together", 1495 "--ignore-matching-lines"); 1496 if (opt->close_file) 1497 die("combined diff and '%s' cannot be used together", 1498 "--output"); 1499 1500 /* nothing to do, if no parents */ 1501 if (!num_parent) 1502 return; 1503 1504 show_log_first = !!rev->loginfo && !rev->no_commit_id; 1505 needsep = 0; 1506 if (show_log_first) { 1507 show_log(rev); 1508 1509 if (rev->verbose_header && opt->output_format && 1510 opt->output_format != DIFF_FORMAT_NO_OUTPUT && 1511 !commit_format_is_empty(rev->commit_format)) 1512 printf("%s%c", diff_line_prefix(opt), 1513 opt->line_termination); 1514 } 1515 1516 diffopts = *opt; 1517 copy_pathspec(&diffopts.pathspec, &opt->pathspec); 1518 diffopts.flags.allow_external = 0; 1519 if (!opt->flags.no_recursive_diff_tree_combined) 1520 diffopts.flags.recursive = 1; 1521 1522 /* find set of paths that everybody touches 1523 * 1524 * NOTE 1525 * 1526 * Diffcore transformations are bound to diff_filespec and logic 1527 * comparing two entries - i.e. they do not apply directly to combine 1528 * diff. 1529 * 1530 * If some of such transformations is requested - we launch generic 1531 * path scanning, which works significantly slower compared to 1532 * simultaneous all-trees-in-one-go scan in find_paths_multitree(). 1533 * 1534 * TODO some of the filters could be ported to work on 1535 * combine_diff_paths - i.e. all functionality that skips paths, so in 1536 * theory, we could end up having only multitree path scanning. 1537 * 1538 * NOTE please keep this semantically in sync with diffcore_std() 1539 */ 1540 need_generic_pathscan = opt->skip_stat_unmatch || 1541 opt->flags.follow_renames || 1542 opt->break_opt != -1 || 1543 opt->detect_rename || 1544 (opt->pickaxe_opts & 1545 (DIFF_PICKAXE_KINDS_MASK & ~DIFF_PICKAXE_KIND_OBJFIND)) || 1546 opt->filter; 1547 1548 if (need_generic_pathscan) { 1549 /* 1550 * NOTE generic case also handles --stat, as it computes 1551 * diff(sha1,parent_i) for all i to do the job, specifically 1552 * for parent0. 1553 */ 1554 paths = find_paths_generic(oid, parents, &diffopts, 1555 rev->combined_all_paths); 1556 } 1557 else { 1558 int stat_opt; 1559 paths = find_paths_multitree(oid, parents, &diffopts); 1560 1561 if (opt->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND) 1562 paths = combined_objfind(opt, paths, num_parent); 1563 1564 /* 1565 * show stat against the first parent even 1566 * when doing combined diff. 1567 */ 1568 stat_opt = opt->output_format & STAT_FORMAT_MASK; 1569 if (stat_opt) { 1570 diffopts.output_format = stat_opt; 1571 1572 diff_tree_oid(&parents->oid[0], oid, "", &diffopts); 1573 diffcore_std(&diffopts); 1574 if (opt->orderfile) 1575 diffcore_order(opt->orderfile); 1576 diff_flush(&diffopts); 1577 } 1578 } 1579 1580 /* find out number of surviving paths */ 1581 for (num_paths = 0, p = paths; p; p = p->next) 1582 num_paths++; 1583 1584 /* order paths according to diffcore_order */ 1585 if (opt->orderfile && num_paths) { 1586 struct obj_order *o; 1587 1588 ALLOC_ARRAY(o, num_paths); 1589 for (i = 0, p = paths; p; p = p->next, i++) 1590 o[i].obj = p; 1591 order_objects(opt->orderfile, path_path, o, num_paths); 1592 for (i = 0; i < num_paths - 1; i++) { 1593 p = o[i].obj; 1594 p->next = o[i+1].obj; 1595 } 1596 1597 p = o[num_paths-1].obj; 1598 p->next = NULL; 1599 paths = o[0].obj; 1600 free(o); 1601 } 1602 1603 1604 if (num_paths) { 1605 if (opt->output_format & (DIFF_FORMAT_RAW | 1606 DIFF_FORMAT_NAME | 1607 DIFF_FORMAT_NAME_STATUS)) { 1608 for (p = paths; p; p = p->next) 1609 show_raw_diff(p, num_parent, rev); 1610 needsep = 1; 1611 } 1612 else if (opt->output_format & STAT_FORMAT_MASK) 1613 needsep = 1; 1614 else if (opt->output_format & DIFF_FORMAT_CALLBACK) 1615 handle_combined_callback(opt, paths, num_parent, num_paths); 1616 1617 if (opt->output_format & DIFF_FORMAT_PATCH) { 1618 if (needsep) 1619 printf("%s%c", diff_line_prefix(opt), 1620 opt->line_termination); 1621 for (p = paths; p; p = p->next) 1622 show_patch_diff(p, num_parent, 0, rev); 1623 } 1624 } 1625 1626 /* Clean things up */ 1627 while (paths) { 1628 struct combine_diff_path *tmp = paths; 1629 paths = paths->next; 1630 for (i = 0; i < num_parent; i++) 1631 free(tmp->parent[i].path); 1632 free(tmp); 1633 } 1634 1635 clear_pathspec(&diffopts.pathspec); 1636} 1637 1638void diff_tree_combined_merge(const struct commit *commit, 1639 struct rev_info *rev) 1640{ 1641 struct commit_list *parent = get_saved_parents(rev, commit); 1642 struct oid_array parents = OID_ARRAY_INIT; 1643 1644 while (parent) { 1645 oid_array_append(&parents, &parent->item->object.oid); 1646 parent = parent->next; 1647 } 1648 diff_tree_combined(&commit->object.oid, &parents, rev); 1649 oid_array_clear(&parents); 1650} 1651 1652struct combine_diff_path *combine_diff_path_new(const char *path, 1653 size_t path_len, 1654 unsigned int mode, 1655 const struct object_id *oid, 1656 size_t num_parents) 1657{ 1658 struct combine_diff_path *p; 1659 size_t parent_len = st_mult(sizeof(p->parent[0]), num_parents); 1660 1661 p = xmalloc(st_add4(sizeof(*p), path_len, 1, parent_len)); 1662 p->path = (char *)&(p->parent[num_parents]); 1663 memcpy(p->path, path, path_len); 1664 p->path[path_len] = 0; 1665 p->next = NULL; 1666 p->mode = mode; 1667 oidcpy(&p->oid, oid); 1668 1669 memset(p->parent, 0, parent_len); 1670 1671 return p; 1672}