|
10 | 10 | #include "commit.h"
|
11 | 11 | #include "alloc.h"
|
12 | 12 | #include "progress.h"
|
| 13 | +#include "hex.h" |
13 | 14 |
|
14 | 15 | #define DEFAULT_PSEUDO_MERGE_DECAY 1.0
|
15 | 16 | #define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
|
@@ -464,3 +465,237 @@ void free_pseudo_merge_map(struct pseudo_merge_map *pm)
|
464 | 465 | }
|
465 | 466 | free(pm->v);
|
466 | 467 | }
|
| 468 | + |
| 469 | +struct pseudo_merge_commit_ext { |
| 470 | + uint32_t nr; |
| 471 | + const unsigned char *ptr; |
| 472 | +}; |
| 473 | + |
| 474 | +static int pseudo_merge_ext_at(const struct pseudo_merge_map *pm, |
| 475 | + struct pseudo_merge_commit_ext *ext, size_t at) |
| 476 | +{ |
| 477 | + if (at >= pm->map_size) |
| 478 | + return error(_("extended pseudo-merge read out-of-bounds " |
| 479 | + "(%"PRIuMAX" >= %"PRIuMAX")"), |
| 480 | + (uintmax_t)at, (uintmax_t)pm->map_size); |
| 481 | + if (at + 4 >= pm->map_size) |
| 482 | + return error(_("extended pseudo-merge entry is too short " |
| 483 | + "(%"PRIuMAX" >= %"PRIuMAX")"), |
| 484 | + (uintmax_t)(at + 4), (uintmax_t)pm->map_size); |
| 485 | + |
| 486 | + ext->nr = get_be32(pm->map + at); |
| 487 | + ext->ptr = pm->map + at + sizeof(uint32_t); |
| 488 | + |
| 489 | + return 0; |
| 490 | +} |
| 491 | + |
| 492 | +struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm, |
| 493 | + struct pseudo_merge *merge) |
| 494 | +{ |
| 495 | + if (!merge->loaded_commits) |
| 496 | + BUG("cannot use unloaded pseudo-merge bitmap"); |
| 497 | + |
| 498 | + if (!merge->loaded_bitmap) { |
| 499 | + size_t at = merge->bitmap_at; |
| 500 | + |
| 501 | + merge->bitmap = read_bitmap(pm->map, pm->map_size, &at); |
| 502 | + merge->loaded_bitmap = 1; |
| 503 | + } |
| 504 | + |
| 505 | + return merge->bitmap; |
| 506 | +} |
| 507 | + |
| 508 | +struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm, |
| 509 | + struct pseudo_merge *merge) |
| 510 | +{ |
| 511 | + if (!merge->loaded_commits) { |
| 512 | + size_t pos = merge->at; |
| 513 | + |
| 514 | + merge->commits = read_bitmap(pm->map, pm->map_size, &pos); |
| 515 | + merge->bitmap_at = pos; |
| 516 | + merge->loaded_commits = 1; |
| 517 | + } |
| 518 | + return merge; |
| 519 | +} |
| 520 | + |
| 521 | +static struct pseudo_merge *pseudo_merge_at(const struct pseudo_merge_map *pm, |
| 522 | + struct object_id *oid, |
| 523 | + size_t want) |
| 524 | +{ |
| 525 | + size_t lo = 0; |
| 526 | + size_t hi = pm->nr; |
| 527 | + |
| 528 | + while (lo < hi) { |
| 529 | + size_t mi = lo + (hi - lo) / 2; |
| 530 | + size_t got = pm->v[mi].at; |
| 531 | + |
| 532 | + if (got == want) |
| 533 | + return use_pseudo_merge(pm, &pm->v[mi]); |
| 534 | + else if (got < want) |
| 535 | + hi = mi; |
| 536 | + else |
| 537 | + lo = mi + 1; |
| 538 | + } |
| 539 | + |
| 540 | + warning(_("could not find pseudo-merge for commit %s at offset %"PRIuMAX), |
| 541 | + oid_to_hex(oid), (uintmax_t)want); |
| 542 | + |
| 543 | + return NULL; |
| 544 | +} |
| 545 | + |
| 546 | +struct pseudo_merge_commit { |
| 547 | + uint32_t commit_pos; |
| 548 | + uint64_t pseudo_merge_ofs; |
| 549 | +}; |
| 550 | + |
| 551 | +#define PSEUDO_MERGE_COMMIT_RAWSZ (sizeof(uint32_t)+sizeof(uint64_t)) |
| 552 | + |
| 553 | +static void read_pseudo_merge_commit_at(struct pseudo_merge_commit *merge, |
| 554 | + const unsigned char *at) |
| 555 | +{ |
| 556 | + merge->commit_pos = get_be32(at); |
| 557 | + merge->pseudo_merge_ofs = get_be64(at + sizeof(uint32_t)); |
| 558 | +} |
| 559 | + |
| 560 | +static int nth_pseudo_merge_ext(const struct pseudo_merge_map *pm, |
| 561 | + struct pseudo_merge_commit_ext *ext, |
| 562 | + struct pseudo_merge_commit *merge, |
| 563 | + uint32_t n) |
| 564 | +{ |
| 565 | + size_t ofs; |
| 566 | + |
| 567 | + if (n >= ext->nr) |
| 568 | + return error(_("extended pseudo-merge lookup out-of-bounds " |
| 569 | + "(%"PRIu32" >= %"PRIu32")"), n, ext->nr); |
| 570 | + |
| 571 | + ofs = get_be64(ext->ptr + st_mult(n, sizeof(uint64_t))); |
| 572 | + if (ofs >= pm->map_size) |
| 573 | + return error(_("out-of-bounds read: (%"PRIuMAX" >= %"PRIuMAX")"), |
| 574 | + (uintmax_t)ofs, (uintmax_t)pm->map_size); |
| 575 | + |
| 576 | + read_pseudo_merge_commit_at(merge, pm->map + ofs); |
| 577 | + |
| 578 | + return 0; |
| 579 | +} |
| 580 | + |
| 581 | +static unsigned apply_pseudo_merge(const struct pseudo_merge_map *pm, |
| 582 | + struct pseudo_merge *merge, |
| 583 | + struct bitmap *result, |
| 584 | + struct bitmap *roots) |
| 585 | +{ |
| 586 | + if (merge->satisfied) |
| 587 | + return 0; |
| 588 | + |
| 589 | + if (!ewah_bitmap_is_subset(merge->commits, roots ? roots : result)) |
| 590 | + return 0; |
| 591 | + |
| 592 | + bitmap_or_ewah(result, pseudo_merge_bitmap(pm, merge)); |
| 593 | + if (roots) |
| 594 | + bitmap_or_ewah(roots, pseudo_merge_bitmap(pm, merge)); |
| 595 | + merge->satisfied = 1; |
| 596 | + |
| 597 | + return 1; |
| 598 | +} |
| 599 | + |
| 600 | +static int pseudo_merge_commit_cmp(const void *va, const void *vb) |
| 601 | +{ |
| 602 | + struct pseudo_merge_commit merge; |
| 603 | + uint32_t key = *(uint32_t*)va; |
| 604 | + |
| 605 | + read_pseudo_merge_commit_at(&merge, vb); |
| 606 | + |
| 607 | + if (key < merge.commit_pos) |
| 608 | + return -1; |
| 609 | + if (key > merge.commit_pos) |
| 610 | + return 1; |
| 611 | + return 0; |
| 612 | +} |
| 613 | + |
| 614 | +static struct pseudo_merge_commit *find_pseudo_merge(const struct pseudo_merge_map *pm, |
| 615 | + uint32_t pos) |
| 616 | +{ |
| 617 | + if (!pm->commits_nr) |
| 618 | + return NULL; |
| 619 | + |
| 620 | + return bsearch(&pos, pm->commits, pm->commits_nr, |
| 621 | + PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp); |
| 622 | +} |
| 623 | + |
| 624 | +int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm, |
| 625 | + struct bitmap *result, |
| 626 | + struct commit *commit, uint32_t commit_pos) |
| 627 | +{ |
| 628 | + struct pseudo_merge *merge; |
| 629 | + struct pseudo_merge_commit *merge_commit; |
| 630 | + int ret = 0; |
| 631 | + |
| 632 | + merge_commit = find_pseudo_merge(pm, commit_pos); |
| 633 | + if (!merge_commit) |
| 634 | + return 0; |
| 635 | + |
| 636 | + if (merge_commit->pseudo_merge_ofs & ((uint64_t)1<<63)) { |
| 637 | + struct pseudo_merge_commit_ext ext = { 0 }; |
| 638 | + off_t ofs = merge_commit->pseudo_merge_ofs & ~((uint64_t)1<<63); |
| 639 | + uint32_t i; |
| 640 | + |
| 641 | + if (pseudo_merge_ext_at(pm, &ext, ofs) < -1) { |
| 642 | + warning(_("could not read extended pseudo-merge table " |
| 643 | + "for commit %s"), |
| 644 | + oid_to_hex(&commit->object.oid)); |
| 645 | + return ret; |
| 646 | + } |
| 647 | + |
| 648 | + for (i = 0; i < ext.nr; i++) { |
| 649 | + if (nth_pseudo_merge_ext(pm, &ext, merge_commit, i) < 0) |
| 650 | + return ret; |
| 651 | + |
| 652 | + merge = pseudo_merge_at(pm, &commit->object.oid, |
| 653 | + merge_commit->pseudo_merge_ofs); |
| 654 | + |
| 655 | + if (!merge) |
| 656 | + return ret; |
| 657 | + |
| 658 | + if (apply_pseudo_merge(pm, merge, result, NULL)) |
| 659 | + ret++; |
| 660 | + } |
| 661 | + } else { |
| 662 | + merge = pseudo_merge_at(pm, &commit->object.oid, |
| 663 | + merge_commit->pseudo_merge_ofs); |
| 664 | + |
| 665 | + if (!merge) |
| 666 | + return ret; |
| 667 | + |
| 668 | + if (apply_pseudo_merge(pm, merge, result, NULL)) |
| 669 | + ret++; |
| 670 | + } |
| 671 | + |
| 672 | + if (ret) |
| 673 | + cascade_pseudo_merges(pm, result, NULL); |
| 674 | + |
| 675 | + return ret; |
| 676 | +} |
| 677 | + |
| 678 | +int cascade_pseudo_merges(const struct pseudo_merge_map *pm, |
| 679 | + struct bitmap *result, |
| 680 | + struct bitmap *roots) |
| 681 | +{ |
| 682 | + unsigned any_satisfied; |
| 683 | + int ret = 0; |
| 684 | + |
| 685 | + do { |
| 686 | + struct pseudo_merge *merge; |
| 687 | + uint32_t i; |
| 688 | + |
| 689 | + any_satisfied = 0; |
| 690 | + |
| 691 | + for (i = 0; i < pm->nr; i++) { |
| 692 | + merge = use_pseudo_merge(pm, &pm->v[i]); |
| 693 | + if (apply_pseudo_merge(pm, merge, result, roots)) { |
| 694 | + any_satisfied |= 1; |
| 695 | + ret++; |
| 696 | + } |
| 697 | + } |
| 698 | + } while (any_satisfied); |
| 699 | + |
| 700 | + return ret; |
| 701 | +} |
0 commit comments