Git fork
at reftables-rust 1349 lines 34 kB view raw
1#define DISABLE_SIGN_COMPARE_WARNINGS 2 3#include "git-compat-util.h" 4#include "diffcore.h" 5#include "line-range.h" 6#include "hex.h" 7#include "tag.h" 8#include "tree.h" 9#include "diff.h" 10#include "commit.h" 11#include "decorate.h" 12#include "repository.h" 13#include "revision.h" 14#include "xdiff-interface.h" 15#include "strbuf.h" 16#include "log-tree.h" 17#include "line-log.h" 18#include "setup.h" 19#include "strvec.h" 20#include "bloom.h" 21#include "tree-walk.h" 22 23static void range_set_grow(struct range_set *rs, size_t extra) 24{ 25 ALLOC_GROW(rs->ranges, rs->nr + extra, rs->alloc); 26} 27 28/* Either initialization would be fine */ 29#define RANGE_SET_INIT {0} 30 31void range_set_init(struct range_set *rs, size_t prealloc) 32{ 33 rs->alloc = rs->nr = 0; 34 rs->ranges = NULL; 35 if (prealloc) 36 range_set_grow(rs, prealloc); 37} 38 39void range_set_release(struct range_set *rs) 40{ 41 FREE_AND_NULL(rs->ranges); 42 rs->alloc = rs->nr = 0; 43} 44 45/* dst must be uninitialized! */ 46static void range_set_copy(struct range_set *dst, struct range_set *src) 47{ 48 range_set_init(dst, src->nr); 49 COPY_ARRAY(dst->ranges, src->ranges, src->nr); 50 dst->nr = src->nr; 51} 52 53static void range_set_move(struct range_set *dst, struct range_set *src) 54{ 55 range_set_release(dst); 56 dst->ranges = src->ranges; 57 dst->nr = src->nr; 58 dst->alloc = src->alloc; 59 src->ranges = NULL; 60 src->alloc = src->nr = 0; 61} 62 63/* tack on a _new_ range _at the end_ */ 64void range_set_append_unsafe(struct range_set *rs, long a, long b) 65{ 66 assert(a <= b); 67 range_set_grow(rs, 1); 68 rs->ranges[rs->nr].start = a; 69 rs->ranges[rs->nr].end = b; 70 rs->nr++; 71} 72 73void range_set_append(struct range_set *rs, long a, long b) 74{ 75 assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a); 76 range_set_append_unsafe(rs, a, b); 77} 78 79static int range_cmp(const void *_r, const void *_s) 80{ 81 const struct range *r = _r; 82 const struct range *s = _s; 83 84 /* this could be simply 'return r.start-s.start', but for the types */ 85 if (r->start == s->start) 86 return 0; 87 if (r->start < s->start) 88 return -1; 89 return 1; 90} 91 92/* 93 * Check that the ranges are non-empty, sorted and non-overlapping 94 */ 95static void range_set_check_invariants(struct range_set *rs) 96{ 97 unsigned int i; 98 99 if (!rs) 100 return; 101 102 if (rs->nr) 103 assert(rs->ranges[0].start < rs->ranges[0].end); 104 105 for (i = 1; i < rs->nr; i++) { 106 assert(rs->ranges[i-1].end < rs->ranges[i].start); 107 assert(rs->ranges[i].start < rs->ranges[i].end); 108 } 109} 110 111/* 112 * In-place pass of sorting and merging the ranges in the range set, 113 * to establish the invariants when we get the ranges from the user 114 */ 115void sort_and_merge_range_set(struct range_set *rs) 116{ 117 unsigned int i; 118 unsigned int o = 0; /* output cursor */ 119 120 QSORT(rs->ranges, rs->nr, range_cmp); 121 122 for (i = 0; i < rs->nr; i++) { 123 if (rs->ranges[i].start == rs->ranges[i].end) 124 continue; 125 if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) { 126 if (rs->ranges[o-1].end < rs->ranges[i].end) 127 rs->ranges[o-1].end = rs->ranges[i].end; 128 } else { 129 rs->ranges[o].start = rs->ranges[i].start; 130 rs->ranges[o].end = rs->ranges[i].end; 131 o++; 132 } 133 } 134 assert(o <= rs->nr); 135 rs->nr = o; 136 137 range_set_check_invariants(rs); 138} 139 140/* 141 * Union of range sets (i.e., sets of line numbers). Used to merge 142 * them when searches meet at a common ancestor. 143 * 144 * This is also where the ranges are consolidated into canonical form: 145 * overlapping and adjacent ranges are merged, and empty ranges are 146 * removed. 147 */ 148static void range_set_union(struct range_set *out, 149 struct range_set *a, struct range_set *b) 150{ 151 unsigned int i = 0, j = 0; 152 struct range *ra = a->ranges; 153 struct range *rb = b->ranges; 154 /* cannot make an alias of out->ranges: it may change during grow */ 155 156 assert(out->nr == 0); 157 while (i < a->nr || j < b->nr) { 158 struct range *new_range; 159 if (i < a->nr && j < b->nr) { 160 if (ra[i].start < rb[j].start) 161 new_range = &ra[i++]; 162 else if (ra[i].start > rb[j].start) 163 new_range = &rb[j++]; 164 else if (ra[i].end < rb[j].end) 165 new_range = &ra[i++]; 166 else 167 new_range = &rb[j++]; 168 } else if (i < a->nr) /* b exhausted */ 169 new_range = &ra[i++]; 170 else /* a exhausted */ 171 new_range = &rb[j++]; 172 if (new_range->start == new_range->end) 173 ; /* empty range */ 174 else if (!out->nr || out->ranges[out->nr-1].end < new_range->start) { 175 range_set_grow(out, 1); 176 out->ranges[out->nr].start = new_range->start; 177 out->ranges[out->nr].end = new_range->end; 178 out->nr++; 179 } else if (out->ranges[out->nr-1].end < new_range->end) { 180 out->ranges[out->nr-1].end = new_range->end; 181 } 182 } 183} 184 185/* 186 * Difference of range sets (out = a \ b). Pass the "interesting" 187 * ranges as 'a' and the target side of the diff as 'b': it removes 188 * the ranges for which the commit is responsible. 189 */ 190static void range_set_difference(struct range_set *out, 191 struct range_set *a, struct range_set *b) 192{ 193 unsigned int i, j = 0; 194 for (i = 0; i < a->nr; i++) { 195 long start = a->ranges[i].start; 196 long end = a->ranges[i].end; 197 while (start < end) { 198 while (j < b->nr && start >= b->ranges[j].end) 199 /* 200 * a: |------- 201 * b: ------| 202 */ 203 j++; 204 if (j >= b->nr || end <= b->ranges[j].start) { 205 /* 206 * b exhausted, or 207 * a: ----| 208 * b: |---- 209 */ 210 range_set_append(out, start, end); 211 break; 212 } 213 if (start >= b->ranges[j].start) { 214 /* 215 * a: |--???? 216 * b: |------| 217 */ 218 start = b->ranges[j].end; 219 } else if (end > b->ranges[j].start) { 220 /* 221 * a: |-----| 222 * b: |--????? 223 */ 224 if (start < b->ranges[j].start) 225 range_set_append(out, start, b->ranges[j].start); 226 start = b->ranges[j].end; 227 } 228 } 229 } 230} 231 232static void diff_ranges_init(struct diff_ranges *diff) 233{ 234 range_set_init(&diff->parent, 0); 235 range_set_init(&diff->target, 0); 236} 237 238static void diff_ranges_release(struct diff_ranges *diff) 239{ 240 range_set_release(&diff->parent); 241 range_set_release(&diff->target); 242} 243 244static void line_log_data_init(struct line_log_data *r) 245{ 246 memset(r, 0, sizeof(struct line_log_data)); 247 range_set_init(&r->ranges, 0); 248} 249 250static void line_log_data_clear(struct line_log_data *r) 251{ 252 range_set_release(&r->ranges); 253 free(r->path); 254 if (r->pair) 255 diff_free_filepair(r->pair); 256 diff_ranges_release(&r->diff); 257} 258 259static void free_line_log_data(struct line_log_data *r) 260{ 261 while (r) { 262 struct line_log_data *next = r->next; 263 line_log_data_clear(r); 264 free(r); 265 r = next; 266 } 267} 268 269static struct line_log_data * 270search_line_log_data(struct line_log_data *list, const char *path, 271 struct line_log_data **insertion_point) 272{ 273 struct line_log_data *p = list; 274 if (insertion_point) 275 *insertion_point = NULL; 276 while (p) { 277 int cmp = strcmp(p->path, path); 278 if (!cmp) 279 return p; 280 if (insertion_point && cmp < 0) 281 *insertion_point = p; 282 p = p->next; 283 } 284 return NULL; 285} 286 287/* 288 * Note: takes ownership of 'path', which happens to be what the only 289 * caller needs. 290 */ 291static void line_log_data_insert(struct line_log_data **list, 292 char *path, 293 long begin, long end) 294{ 295 struct line_log_data *ip; 296 struct line_log_data *p = search_line_log_data(*list, path, &ip); 297 298 if (p) { 299 range_set_append_unsafe(&p->ranges, begin, end); 300 free(path); 301 return; 302 } 303 304 CALLOC_ARRAY(p, 1); 305 p->path = path; 306 range_set_append(&p->ranges, begin, end); 307 if (ip) { 308 p->next = ip->next; 309 ip->next = p; 310 } else { 311 p->next = *list; 312 *list = p; 313 } 314} 315 316struct collect_diff_cbdata { 317 struct diff_ranges *diff; 318}; 319 320static int collect_diff_cb(long start_a, long count_a, 321 long start_b, long count_b, 322 void *data) 323{ 324 struct collect_diff_cbdata *d = data; 325 326 if (count_a >= 0) 327 range_set_append(&d->diff->parent, start_a, start_a + count_a); 328 if (count_b >= 0) 329 range_set_append(&d->diff->target, start_b, start_b + count_b); 330 331 return 0; 332} 333 334static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out) 335{ 336 struct collect_diff_cbdata cbdata = {NULL}; 337 xpparam_t xpp; 338 xdemitconf_t xecfg; 339 xdemitcb_t ecb; 340 341 memset(&xpp, 0, sizeof(xpp)); 342 memset(&xecfg, 0, sizeof(xecfg)); 343 xecfg.ctxlen = xecfg.interhunkctxlen = 0; 344 345 cbdata.diff = out; 346 xecfg.hunk_func = collect_diff_cb; 347 memset(&ecb, 0, sizeof(ecb)); 348 ecb.priv = &cbdata; 349 return xdi_diff(parent, target, &xpp, &xecfg, &ecb); 350} 351 352/* 353 * These are handy for debugging. Removing them with #if 0 silences 354 * the "unused function" warning. 355 */ 356#if 0 357static void dump_range_set(struct range_set *rs, const char *desc) 358{ 359 int i; 360 printf("range set %s (%d items):\n", desc, rs->nr); 361 for (i = 0; i < rs->nr; i++) 362 printf("\t[%ld,%ld]\n", rs->ranges[i].start, rs->ranges[i].end); 363} 364 365static void dump_line_log_data(struct line_log_data *r) 366{ 367 char buf[4096]; 368 while (r) { 369 snprintf(buf, 4096, "file %s\n", r->path); 370 dump_range_set(&r->ranges, buf); 371 r = r->next; 372 } 373} 374 375static void dump_diff_ranges(struct diff_ranges *diff, const char *desc) 376{ 377 int i; 378 assert(diff->parent.nr == diff->target.nr); 379 printf("diff ranges %s (%d items):\n", desc, diff->parent.nr); 380 printf("\tparent\ttarget\n"); 381 for (i = 0; i < diff->parent.nr; i++) { 382 printf("\t[%ld,%ld]\t[%ld,%ld]\n", 383 diff->parent.ranges[i].start, 384 diff->parent.ranges[i].end, 385 diff->target.ranges[i].start, 386 diff->target.ranges[i].end); 387 } 388} 389#endif 390 391 392static int ranges_overlap(struct range *a, struct range *b) 393{ 394 return !(a->end <= b->start || b->end <= a->start); 395} 396 397/* 398 * Given a diff and the set of interesting ranges, determine all hunks 399 * of the diff which touch (overlap) at least one of the interesting 400 * ranges in the target. 401 */ 402static void diff_ranges_filter_touched(struct diff_ranges *out, 403 struct diff_ranges *diff, 404 struct range_set *rs) 405{ 406 unsigned int i, j = 0; 407 408 assert(out->target.nr == 0); 409 410 for (i = 0; i < diff->target.nr; i++) { 411 while (diff->target.ranges[i].start >= rs->ranges[j].end) { 412 j++; 413 if (j == rs->nr) 414 return; 415 } 416 if (ranges_overlap(&diff->target.ranges[i], &rs->ranges[j])) { 417 range_set_append(&out->parent, 418 diff->parent.ranges[i].start, 419 diff->parent.ranges[i].end); 420 range_set_append(&out->target, 421 diff->target.ranges[i].start, 422 diff->target.ranges[i].end); 423 } 424 } 425} 426 427/* 428 * Adjust the line counts in 'rs' to account for the lines 429 * added/removed in the diff. 430 */ 431static void range_set_shift_diff(struct range_set *out, 432 struct range_set *rs, 433 struct diff_ranges *diff) 434{ 435 unsigned int i, j = 0; 436 long offset = 0; 437 struct range *src = rs->ranges; 438 struct range *target = diff->target.ranges; 439 struct range *parent = diff->parent.ranges; 440 441 for (i = 0; i < rs->nr; i++) { 442 while (j < diff->target.nr && src[i].start >= target[j].start) { 443 offset += (parent[j].end-parent[j].start) 444 - (target[j].end-target[j].start); 445 j++; 446 } 447 range_set_append(out, src[i].start+offset, src[i].end+offset); 448 } 449} 450 451/* 452 * Given a diff and the set of interesting ranges, map the ranges 453 * across the diff. That is: observe that the target commit takes 454 * blame for all the + (target-side) ranges. So for every pair of 455 * ranges in the diff that was touched, we remove the latter and add 456 * its parent side. 457 */ 458static void range_set_map_across_diff(struct range_set *out, 459 struct range_set *rs, 460 struct diff_ranges *diff, 461 struct diff_ranges **touched_out) 462{ 463 struct diff_ranges *touched = xmalloc(sizeof(*touched)); 464 struct range_set tmp1 = RANGE_SET_INIT; 465 struct range_set tmp2 = RANGE_SET_INIT; 466 467 diff_ranges_init(touched); 468 diff_ranges_filter_touched(touched, diff, rs); 469 range_set_difference(&tmp1, rs, &touched->target); 470 range_set_shift_diff(&tmp2, &tmp1, diff); 471 range_set_union(out, &tmp2, &touched->parent); 472 range_set_release(&tmp1); 473 range_set_release(&tmp2); 474 475 *touched_out = touched; 476} 477 478static struct commit *check_single_commit(struct rev_info *revs) 479{ 480 struct object *commit = NULL; 481 int found = -1; 482 int i; 483 484 for (i = 0; i < revs->pending.nr; i++) { 485 struct object *obj = revs->pending.objects[i].item; 486 if (obj->flags & UNINTERESTING) 487 continue; 488 obj = deref_tag(revs->repo, obj, NULL, 0); 489 if (!obj || obj->type != OBJ_COMMIT) 490 die("Non commit %s?", revs->pending.objects[i].name); 491 if (commit) 492 die("More than one commit to dig from: %s and %s?", 493 revs->pending.objects[i].name, 494 revs->pending.objects[found].name); 495 commit = obj; 496 found = i; 497 } 498 499 if (!commit) 500 die("No commit specified?"); 501 502 return (struct commit *) commit; 503} 504 505static void fill_blob_sha1(struct repository *r, struct commit *commit, 506 struct diff_filespec *spec) 507{ 508 unsigned short mode; 509 struct object_id oid; 510 511 if (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode)) 512 die("There is no path %s in the commit", spec->path); 513 fill_filespec(spec, &oid, 1, mode); 514 515 return; 516} 517 518static void fill_line_ends(struct repository *r, 519 struct diff_filespec *spec, 520 long *lines, 521 unsigned long **line_ends) 522{ 523 int num = 0, size = 50; 524 long cur = 0; 525 unsigned long *ends = NULL; 526 char *data = NULL; 527 528 if (diff_populate_filespec(r, spec, NULL)) 529 die("Cannot read blob %s", oid_to_hex(&spec->oid)); 530 531 ALLOC_ARRAY(ends, size); 532 ends[cur++] = 0; 533 data = spec->data; 534 while (num < spec->size) { 535 if (data[num] == '\n' || num == spec->size - 1) { 536 ALLOC_GROW(ends, (cur + 1), size); 537 ends[cur++] = num; 538 } 539 num++; 540 } 541 542 /* shrink the array to fit the elements */ 543 REALLOC_ARRAY(ends, cur); 544 *lines = cur-1; 545 *line_ends = ends; 546} 547 548struct nth_line_cb { 549 struct diff_filespec *spec; 550 long lines; 551 unsigned long *line_ends; 552}; 553 554static const char *nth_line(void *data, long line) 555{ 556 struct nth_line_cb *d = data; 557 assert(d && line <= d->lines); 558 assert(d->spec && d->spec->data); 559 560 if (line == 0) 561 return (char *)d->spec->data; 562 else 563 return (char *)d->spec->data + d->line_ends[line] + 1; 564} 565 566static struct line_log_data * 567parse_lines(struct repository *r, struct commit *commit, 568 const char *prefix, struct string_list *args) 569{ 570 long lines = 0; 571 unsigned long *ends = NULL; 572 struct nth_line_cb cb_data; 573 struct string_list_item *item; 574 struct line_log_data *ranges = NULL; 575 struct line_log_data *p; 576 577 for_each_string_list_item(item, args) { 578 const char *name_part; 579 char *range_part; 580 char *full_name; 581 struct diff_filespec *spec; 582 long begin = 0, end = 0; 583 long anchor; 584 585 name_part = skip_range_arg(item->string, r->index); 586 if (!name_part || *name_part != ':' || !name_part[1]) 587 die("-L argument not 'start,end:file' or ':funcname:file': %s", 588 item->string); 589 range_part = xstrndup(item->string, name_part - item->string); 590 name_part++; 591 592 full_name = prefix_path(prefix, prefix ? strlen(prefix) : 0, 593 name_part); 594 595 spec = alloc_filespec(full_name); 596 fill_blob_sha1(r, commit, spec); 597 fill_line_ends(r, spec, &lines, &ends); 598 cb_data.spec = spec; 599 cb_data.lines = lines; 600 cb_data.line_ends = ends; 601 602 p = search_line_log_data(ranges, full_name, NULL); 603 if (p && p->ranges.nr) 604 anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1; 605 else 606 anchor = 1; 607 608 if (parse_range_arg(range_part, nth_line, &cb_data, 609 lines, anchor, &begin, &end, 610 full_name, r->index)) 611 die("malformed -L argument '%s'", range_part); 612 if ((!lines && (begin || end)) || lines < begin) 613 die("file %s has only %lu lines", name_part, lines); 614 if (begin < 1) 615 begin = 1; 616 if (end < 1 || lines < end) 617 end = lines; 618 begin--; 619 line_log_data_insert(&ranges, full_name, begin, end); 620 621 free_filespec(spec); 622 FREE_AND_NULL(ends); 623 free(range_part); 624 } 625 626 for (p = ranges; p; p = p->next) 627 sort_and_merge_range_set(&p->ranges); 628 629 return ranges; 630} 631 632static struct line_log_data *line_log_data_copy_one(struct line_log_data *r) 633{ 634 struct line_log_data *ret = xmalloc(sizeof(*ret)); 635 636 assert(r); 637 line_log_data_init(ret); 638 range_set_copy(&ret->ranges, &r->ranges); 639 640 ret->path = xstrdup(r->path); 641 642 return ret; 643} 644 645static struct line_log_data * 646line_log_data_copy(struct line_log_data *r) 647{ 648 struct line_log_data *ret = NULL; 649 struct line_log_data *tmp = NULL, *prev = NULL; 650 651 assert(r); 652 ret = tmp = prev = line_log_data_copy_one(r); 653 r = r->next; 654 while (r) { 655 tmp = line_log_data_copy_one(r); 656 prev->next = tmp; 657 prev = tmp; 658 r = r->next; 659 } 660 661 return ret; 662} 663 664/* merge two range sets across files */ 665static struct line_log_data *line_log_data_merge(struct line_log_data *a, 666 struct line_log_data *b) 667{ 668 struct line_log_data *head = NULL, **pp = &head; 669 670 while (a || b) { 671 struct line_log_data *src; 672 struct line_log_data *src2 = NULL; 673 struct line_log_data *d; 674 int cmp; 675 if (!a) 676 cmp = 1; 677 else if (!b) 678 cmp = -1; 679 else 680 cmp = strcmp(a->path, b->path); 681 if (cmp < 0) { 682 src = a; 683 a = a->next; 684 } else if (cmp == 0) { 685 src = a; 686 a = a->next; 687 src2 = b; 688 b = b->next; 689 } else { 690 src = b; 691 b = b->next; 692 } 693 d = xmalloc(sizeof(struct line_log_data)); 694 line_log_data_init(d); 695 d->path = xstrdup(src->path); 696 *pp = d; 697 pp = &d->next; 698 if (src2) 699 range_set_union(&d->ranges, &src->ranges, &src2->ranges); 700 else 701 range_set_copy(&d->ranges, &src->ranges); 702 } 703 704 return head; 705} 706 707static void add_line_range(struct rev_info *revs, struct commit *commit, 708 struct line_log_data *range) 709{ 710 struct line_log_data *old_line = NULL; 711 struct line_log_data *new_line = NULL; 712 713 old_line = lookup_decoration(&revs->line_log_data, &commit->object); 714 if (old_line && range) { 715 new_line = line_log_data_merge(old_line, range); 716 free_line_log_data(old_line); 717 } else if (range) 718 new_line = line_log_data_copy(range); 719 720 if (new_line) 721 add_decoration(&revs->line_log_data, &commit->object, new_line); 722} 723 724static void clear_commit_line_range(struct rev_info *revs, struct commit *commit) 725{ 726 struct line_log_data *r; 727 r = lookup_decoration(&revs->line_log_data, &commit->object); 728 if (!r) 729 return; 730 free_line_log_data(r); 731 add_decoration(&revs->line_log_data, &commit->object, NULL); 732} 733 734static struct line_log_data *lookup_line_range(struct rev_info *revs, 735 struct commit *commit) 736{ 737 struct line_log_data *ret = NULL; 738 struct line_log_data *d; 739 740 ret = lookup_decoration(&revs->line_log_data, &commit->object); 741 742 for (d = ret; d; d = d->next) 743 range_set_check_invariants(&d->ranges); 744 745 return ret; 746} 747 748static int same_paths_in_pathspec_and_range(struct pathspec *pathspec, 749 struct line_log_data *range) 750{ 751 int i; 752 struct line_log_data *r; 753 754 for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next) 755 if (strcmp(pathspec->items[i].match, r->path)) 756 return 0; 757 if (i < pathspec->nr || r) 758 /* different number of pathspec items and ranges */ 759 return 0; 760 761 return 1; 762} 763 764static void parse_pathspec_from_ranges(struct pathspec *pathspec, 765 struct line_log_data *range) 766{ 767 struct line_log_data *r; 768 struct strvec array = STRVEC_INIT; 769 770 for (r = range; r; r = r->next) 771 strvec_push(&array, r->path); 772 773 parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", array.v); 774 775 strvec_clear(&array); 776} 777 778void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args) 779{ 780 struct commit *commit = NULL; 781 struct line_log_data *range; 782 783 commit = check_single_commit(rev); 784 range = parse_lines(rev->diffopt.repo, commit, prefix, args); 785 add_line_range(rev, commit, range); 786 787 parse_pathspec_from_ranges(&rev->diffopt.pathspec, range); 788 789 free_line_log_data(range); 790} 791 792static void move_diff_queue(struct diff_queue_struct *dst, 793 struct diff_queue_struct *src) 794{ 795 assert(src != dst); 796 memcpy(dst, src, sizeof(*dst)); 797 diff_queue_init(src); 798} 799 800static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions) 801{ 802 int i; 803 struct diff_queue_struct outq = DIFF_QUEUE_INIT; 804 805 for (i = 0; i < diff_queued_diff.nr; i++) { 806 struct diff_filepair *p = diff_queued_diff.queue[i]; 807 struct line_log_data *rg = NULL; 808 809 if (!DIFF_FILE_VALID(p->two)) { 810 if (keep_deletions) 811 diff_q(&outq, p); 812 else 813 diff_free_filepair(p); 814 continue; 815 } 816 for (rg = range; rg; rg = rg->next) { 817 if (!strcmp(rg->path, p->two->path)) 818 break; 819 } 820 if (rg) 821 diff_q(&outq, p); 822 else 823 diff_free_filepair(p); 824 } 825 free(diff_queued_diff.queue); 826 diff_queued_diff = outq; 827} 828 829static inline int diff_might_be_rename(void) 830{ 831 int i; 832 for (i = 0; i < diff_queued_diff.nr; i++) 833 if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) { 834 /* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */ 835 /* diff_queued_diff.queue[i]->two->path); */ 836 return 1; 837 } 838 return 0; 839} 840 841static void queue_diffs(struct line_log_data *range, 842 struct diff_options *opt, 843 struct diff_queue_struct *queue, 844 struct commit *commit, struct commit *parent) 845{ 846 struct object_id *tree_oid, *parent_tree_oid; 847 848 assert(commit); 849 850 tree_oid = get_commit_tree_oid(commit); 851 parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL; 852 853 if (opt->detect_rename && 854 !same_paths_in_pathspec_and_range(&opt->pathspec, range)) { 855 clear_pathspec(&opt->pathspec); 856 parse_pathspec_from_ranges(&opt->pathspec, range); 857 } 858 diff_queue_clear(&diff_queued_diff); 859 diff_tree_oid(parent_tree_oid, tree_oid, "", opt); 860 if (opt->detect_rename && diff_might_be_rename()) { 861 /* must look at the full tree diff to detect renames */ 862 clear_pathspec(&opt->pathspec); 863 diff_queue_clear(&diff_queued_diff); 864 865 diff_tree_oid(parent_tree_oid, tree_oid, "", opt); 866 867 filter_diffs_for_paths(range, 1); 868 diffcore_std(opt); 869 filter_diffs_for_paths(range, 0); 870 } 871 move_diff_queue(queue, &diff_queued_diff); 872} 873 874static char *get_nth_line(long line, unsigned long *ends, void *data) 875{ 876 if (line == 0) 877 return (char *)data; 878 else 879 return (char *)data + ends[line] + 1; 880} 881 882static void print_line(const char *prefix, char first, 883 long line, unsigned long *ends, void *data, 884 const char *color, const char *reset, FILE *file) 885{ 886 char *begin = get_nth_line(line, ends, data); 887 char *end = get_nth_line(line+1, ends, data); 888 int had_nl = 0; 889 890 if (end > begin && end[-1] == '\n') { 891 end--; 892 had_nl = 1; 893 } 894 895 fputs(prefix, file); 896 fputs(color, file); 897 putc(first, file); 898 fwrite(begin, 1, end-begin, file); 899 fputs(reset, file); 900 putc('\n', file); 901 if (!had_nl) 902 fputs("\\ No newline at end of file\n", file); 903} 904 905static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range) 906{ 907 unsigned int i, j = 0; 908 long p_lines, t_lines; 909 unsigned long *p_ends = NULL, *t_ends = NULL; 910 struct diff_filepair *pair = range->pair; 911 struct diff_ranges *diff = &range->diff; 912 913 struct diff_options *opt = &rev->diffopt; 914 const char *prefix = diff_line_prefix(opt); 915 const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET); 916 const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO); 917 const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO); 918 const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD); 919 const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW); 920 const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT); 921 922 if (!pair || !diff) 923 goto out; 924 925 if (pair->one->oid_valid) 926 fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends); 927 fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends); 928 929 fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset); 930 fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta, 931 pair->one->oid_valid ? "a/" : "", 932 pair->one->oid_valid ? pair->one->path : "/dev/null", 933 c_reset); 934 fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset); 935 for (i = 0; i < range->ranges.nr; i++) { 936 long p_start, p_end; 937 long t_start = range->ranges.ranges[i].start; 938 long t_end = range->ranges.ranges[i].end; 939 long t_cur = t_start; 940 unsigned int j_last; 941 942 /* 943 * If a diff range touches multiple line ranges, then all 944 * those line ranges should be shown, so take a step back if 945 * the current line range is still in the previous diff range 946 * (even if only partially). 947 */ 948 if (j > 0 && diff->target.ranges[j-1].end > t_start) 949 j--; 950 951 while (j < diff->target.nr && diff->target.ranges[j].end < t_start) 952 j++; 953 if (j == diff->target.nr || diff->target.ranges[j].start >= t_end) 954 continue; 955 956 /* Scan ahead to determine the last diff that falls in this range */ 957 j_last = j; 958 while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end) 959 j_last++; 960 if (j_last > j) 961 j_last--; 962 963 /* 964 * Compute parent hunk headers: we know that the diff 965 * has the correct line numbers (but not all hunks). 966 * So it suffices to shift the start/end according to 967 * the line numbers of the first/last hunk(s) that 968 * fall in this range. 969 */ 970 if (t_start < diff->target.ranges[j].start) 971 p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start); 972 else 973 p_start = diff->parent.ranges[j].start; 974 if (t_end > diff->target.ranges[j_last].end) 975 p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end); 976 else 977 p_end = diff->parent.ranges[j_last].end; 978 979 if (!p_start && !p_end) { 980 p_start = -1; 981 p_end = -1; 982 } 983 984 /* Now output a diff hunk for this range */ 985 fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n", 986 prefix, c_frag, 987 p_start+1, p_end-p_start, t_start+1, t_end-t_start, 988 c_reset); 989 while (j < diff->target.nr && diff->target.ranges[j].start < t_end) { 990 int k; 991 for (; t_cur < diff->target.ranges[j].start; t_cur++) 992 print_line(prefix, ' ', t_cur, t_ends, pair->two->data, 993 c_context, c_reset, opt->file); 994 for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++) 995 print_line(prefix, '-', k, p_ends, pair->one->data, 996 c_old, c_reset, opt->file); 997 for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++) 998 print_line(prefix, '+', t_cur, t_ends, pair->two->data, 999 c_new, c_reset, opt->file); 1000 j++; 1001 } 1002 for (; t_cur < t_end; t_cur++) 1003 print_line(prefix, ' ', t_cur, t_ends, pair->two->data, 1004 c_context, c_reset, opt->file); 1005 } 1006 1007out: 1008 free(p_ends); 1009 free(t_ends); 1010} 1011 1012/* 1013 * NEEDSWORK: manually building a diff here is not the Right 1014 * Thing(tm). log -L should be built into the diff pipeline. 1015 */ 1016static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range) 1017{ 1018 const char *prefix = diff_line_prefix(&rev->diffopt); 1019 1020 fprintf(rev->diffopt.file, "%s\n", prefix); 1021 1022 while (range) { 1023 dump_diff_hacky_one(rev, range); 1024 range = range->next; 1025 } 1026} 1027 1028/* 1029 * Unlike most other functions, this destructively operates on 1030 * 'range'. 1031 */ 1032static int process_diff_filepair(struct rev_info *rev, 1033 struct diff_filepair *pair, 1034 struct line_log_data *range, 1035 struct diff_ranges **diff_out) 1036{ 1037 struct line_log_data *rg = range; 1038 struct range_set tmp; 1039 struct diff_ranges diff; 1040 mmfile_t file_parent, file_target; 1041 char *parent_data_to_free = NULL; 1042 1043 assert(pair->two->path); 1044 while (rg) { 1045 assert(rg->path); 1046 if (!strcmp(rg->path, pair->two->path)) 1047 break; 1048 rg = rg->next; 1049 } 1050 1051 if (!rg) 1052 return 0; 1053 if (rg->ranges.nr == 0) 1054 return 0; 1055 1056 assert(pair->two->oid_valid); 1057 diff_populate_filespec(rev->diffopt.repo, pair->two, NULL); 1058 file_target.ptr = pair->two->data; 1059 file_target.size = pair->two->size; 1060 1061 if (pair->one->oid_valid) { 1062 diff_populate_filespec(rev->diffopt.repo, pair->one, NULL); 1063 file_parent.ptr = pair->one->data; 1064 file_parent.size = pair->one->size; 1065 } else { 1066 file_parent.ptr = parent_data_to_free = xstrdup(""); 1067 file_parent.size = 0; 1068 } 1069 1070 diff_ranges_init(&diff); 1071 if (collect_diff(&file_parent, &file_target, &diff)) 1072 die("unable to generate diff for %s", pair->one->path); 1073 1074 /* NEEDSWORK should apply some heuristics to prevent mismatches */ 1075 free(rg->path); 1076 rg->path = xstrdup(pair->one->path); 1077 1078 range_set_init(&tmp, 0); 1079 range_set_map_across_diff(&tmp, &rg->ranges, &diff, diff_out); 1080 range_set_release(&rg->ranges); 1081 range_set_move(&rg->ranges, &tmp); 1082 1083 diff_ranges_release(&diff); 1084 1085 free(parent_data_to_free); 1086 return ((*diff_out)->parent.nr > 0); 1087} 1088 1089static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair) 1090{ 1091 struct diff_filepair *new_filepair = xmalloc(sizeof(struct diff_filepair)); 1092 new_filepair->one = pair->one; 1093 new_filepair->two = pair->two; 1094 new_filepair->one->count++; 1095 new_filepair->two->count++; 1096 return new_filepair; 1097} 1098 1099static int process_all_files(struct line_log_data **range_out, 1100 struct rev_info *rev, 1101 struct diff_queue_struct *queue, 1102 struct line_log_data *range) 1103{ 1104 int i, changed = 0; 1105 1106 *range_out = line_log_data_copy(range); 1107 1108 for (i = 0; i < queue->nr; i++) { 1109 struct diff_ranges *pairdiff = NULL; 1110 struct diff_filepair *pair = queue->queue[i]; 1111 if (process_diff_filepair(rev, pair, *range_out, &pairdiff)) { 1112 /* 1113 * Store away the diff for later output. We 1114 * tuck it in the ranges we got as _input_, 1115 * since that's the commit that caused the 1116 * diff. 1117 * 1118 * NEEDSWORK not enough when we get around to 1119 * doing something interesting with merges; 1120 * currently each invocation on a merge parent 1121 * trashes the previous one's diff. 1122 * 1123 * NEEDSWORK tramples over data structures not owned here 1124 */ 1125 struct line_log_data *rg = range; 1126 changed++; 1127 while (rg && strcmp(rg->path, pair->two->path)) 1128 rg = rg->next; 1129 assert(rg); 1130 if (rg->pair) 1131 diff_free_filepair(rg->pair); 1132 rg->pair = diff_filepair_dup(queue->queue[i]); 1133 diff_ranges_release(&rg->diff); 1134 memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges)); 1135 FREE_AND_NULL(pairdiff); 1136 } 1137 1138 if (pairdiff) { 1139 diff_ranges_release(pairdiff); 1140 free(pairdiff); 1141 } 1142 } 1143 1144 return changed; 1145} 1146 1147int line_log_print(struct rev_info *rev, struct commit *commit) 1148{ 1149 1150 show_log(rev); 1151 if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) { 1152 struct line_log_data *range = lookup_line_range(rev, commit); 1153 dump_diff_hacky(rev, range); 1154 } 1155 return 1; 1156} 1157 1158static int bloom_filter_check(struct rev_info *rev, 1159 struct commit *commit, 1160 struct line_log_data *range) 1161{ 1162 struct bloom_filter *filter; 1163 struct bloom_key key; 1164 int result = 0; 1165 1166 if (!commit->parents) 1167 return 1; 1168 1169 if (!rev->bloom_filter_settings || 1170 !(filter = get_bloom_filter(rev->repo, commit))) 1171 return 1; 1172 1173 if (!range) 1174 return 0; 1175 1176 while (!result && range) { 1177 bloom_key_fill(&key, range->path, strlen(range->path), 1178 rev->bloom_filter_settings); 1179 1180 if (bloom_filter_contains(filter, &key, rev->bloom_filter_settings)) 1181 result = 1; 1182 1183 bloom_key_clear(&key); 1184 range = range->next; 1185 } 1186 1187 return result; 1188} 1189 1190static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit, 1191 struct line_log_data *range) 1192{ 1193 struct commit *parent = NULL; 1194 struct diff_queue_struct queue = DIFF_QUEUE_INIT; 1195 struct line_log_data *parent_range; 1196 int changed; 1197 1198 if (commit->parents) 1199 parent = commit->parents->item; 1200 1201 queue_diffs(range, &rev->diffopt, &queue, commit, parent); 1202 changed = process_all_files(&parent_range, rev, &queue, range); 1203 1204 if (parent) 1205 add_line_range(rev, parent, parent_range); 1206 free_line_log_data(parent_range); 1207 diff_queue_clear(&queue); 1208 return changed; 1209} 1210 1211static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit, 1212 struct line_log_data *range) 1213{ 1214 struct line_log_data **cand; 1215 struct commit_list *p; 1216 int i; 1217 int nparents = commit_list_count(commit->parents); 1218 int ret; 1219 1220 if (nparents > 1 && rev->first_parent_only) 1221 nparents = 1; 1222 1223 CALLOC_ARRAY(cand, nparents); 1224 1225 for (p = commit->parents, i = 0; 1226 p && i < nparents; 1227 p = p->next, i++) { 1228 struct commit *parent = p->item; 1229 struct diff_queue_struct diffqueue = DIFF_QUEUE_INIT; 1230 int changed; 1231 1232 queue_diffs(range, &rev->diffopt, &diffqueue, commit, parent); 1233 1234 changed = process_all_files(&cand[i], rev, &diffqueue, range); 1235 diff_queue_clear(&diffqueue); 1236 if (!changed) { 1237 /* 1238 * This parent can take all the blame, so we 1239 * don't follow any other path in history 1240 */ 1241 add_line_range(rev, parent, cand[i]); 1242 free_commit_list(commit->parents); 1243 commit_list_append(parent, &commit->parents); 1244 1245 ret = 0; 1246 goto out; 1247 } 1248 } 1249 1250 /* 1251 * No single parent took the blame. We add the candidates 1252 * from the above loop to the parents. 1253 */ 1254 for (p = commit->parents, i = 0; 1255 p && i < nparents; 1256 p = p->next, i++) 1257 add_line_range(rev, p->item, cand[i]); 1258 1259 ret = 1; 1260 1261out: 1262 clear_commit_line_range(rev, commit); 1263 for (i = 0; i < nparents; i++) { 1264 if (!cand[i]) 1265 continue; 1266 line_log_data_clear(cand[i]); 1267 free(cand[i]); 1268 } 1269 free(cand); 1270 return ret; 1271 1272 /* NEEDSWORK evil merge detection stuff */ 1273} 1274 1275int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit) 1276{ 1277 struct line_log_data *range = lookup_line_range(rev, commit); 1278 int changed = 0; 1279 1280 if (range) { 1281 if (commit->parents && !bloom_filter_check(rev, commit, range)) { 1282 struct line_log_data *prange = line_log_data_copy(range); 1283 add_line_range(rev, commit->parents->item, prange); 1284 clear_commit_line_range(rev, commit); 1285 } else if (commit->parents && commit->parents->next) 1286 changed = process_ranges_merge_commit(rev, commit, range); 1287 else 1288 changed = process_ranges_ordinary_commit(rev, commit, range); 1289 } 1290 1291 if (!changed) 1292 commit->object.flags |= TREESAME; 1293 1294 return changed; 1295} 1296 1297static enum rewrite_result line_log_rewrite_one(struct rev_info *rev UNUSED, 1298 struct commit **pp) 1299{ 1300 for (;;) { 1301 struct commit *p = *pp; 1302 if (p->parents && p->parents->next) 1303 return rewrite_one_ok; 1304 if (p->object.flags & UNINTERESTING) 1305 return rewrite_one_ok; 1306 if (!(p->object.flags & TREESAME)) 1307 return rewrite_one_ok; 1308 if (!p->parents) 1309 return rewrite_one_noparents; 1310 *pp = p->parents->item; 1311 } 1312} 1313 1314int line_log_filter(struct rev_info *rev) 1315{ 1316 struct commit *commit; 1317 struct commit_list *list = rev->commits; 1318 struct commit_list *out = NULL, **pp = &out; 1319 1320 while (list) { 1321 struct commit_list *to_free = NULL; 1322 commit = list->item; 1323 if (line_log_process_ranges_arbitrary_commit(rev, commit)) { 1324 *pp = list; 1325 pp = &list->next; 1326 } else 1327 to_free = list; 1328 list = list->next; 1329 free(to_free); 1330 } 1331 *pp = NULL; 1332 1333 for (list = out; list; list = list->next) 1334 rewrite_parents(rev, list->item, line_log_rewrite_one); 1335 1336 rev->commits = out; 1337 1338 return 0; 1339} 1340 1341static void free_void_line_log_data(void *data) 1342{ 1343 free_line_log_data(data); 1344} 1345 1346void line_log_free(struct rev_info *rev) 1347{ 1348 clear_decoration(&rev->line_log_data, free_void_line_log_data); 1349}