|
1 | 1 | #include "cache.h" |
2 | 2 | #include "mru.h" |
3 | 3 | #include "pack.h" |
| 4 | +#include "dir.h" |
| 5 | +#include "mergesort.h" |
| 6 | +#include "packfile.h" |
4 | 7 |
|
5 | 8 | char *odb_pack_name(struct strbuf *buf, |
6 | 9 | const unsigned char *sha1, |
@@ -667,3 +670,217 @@ void install_packed_git(struct packed_git *pack) |
667 | 670 | pack->next = packed_git; |
668 | 671 | packed_git = pack; |
669 | 672 | } |
| 673 | + |
| 674 | +void (*report_garbage)(unsigned seen_bits, const char *path); |
| 675 | + |
| 676 | +static void report_helper(const struct string_list *list, |
| 677 | + int seen_bits, int first, int last) |
| 678 | +{ |
| 679 | + if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX)) |
| 680 | + return; |
| 681 | + |
| 682 | + for (; first < last; first++) |
| 683 | + report_garbage(seen_bits, list->items[first].string); |
| 684 | +} |
| 685 | + |
| 686 | +static void report_pack_garbage(struct string_list *list) |
| 687 | +{ |
| 688 | + int i, baselen = -1, first = 0, seen_bits = 0; |
| 689 | + |
| 690 | + if (!report_garbage) |
| 691 | + return; |
| 692 | + |
| 693 | + string_list_sort(list); |
| 694 | + |
| 695 | + for (i = 0; i < list->nr; i++) { |
| 696 | + const char *path = list->items[i].string; |
| 697 | + if (baselen != -1 && |
| 698 | + strncmp(path, list->items[first].string, baselen)) { |
| 699 | + report_helper(list, seen_bits, first, i); |
| 700 | + baselen = -1; |
| 701 | + seen_bits = 0; |
| 702 | + } |
| 703 | + if (baselen == -1) { |
| 704 | + const char *dot = strrchr(path, '.'); |
| 705 | + if (!dot) { |
| 706 | + report_garbage(PACKDIR_FILE_GARBAGE, path); |
| 707 | + continue; |
| 708 | + } |
| 709 | + baselen = dot - path + 1; |
| 710 | + first = i; |
| 711 | + } |
| 712 | + if (!strcmp(path + baselen, "pack")) |
| 713 | + seen_bits |= 1; |
| 714 | + else if (!strcmp(path + baselen, "idx")) |
| 715 | + seen_bits |= 2; |
| 716 | + } |
| 717 | + report_helper(list, seen_bits, first, list->nr); |
| 718 | +} |
| 719 | + |
| 720 | +static void prepare_packed_git_one(char *objdir, int local) |
| 721 | +{ |
| 722 | + struct strbuf path = STRBUF_INIT; |
| 723 | + size_t dirnamelen; |
| 724 | + DIR *dir; |
| 725 | + struct dirent *de; |
| 726 | + struct string_list garbage = STRING_LIST_INIT_DUP; |
| 727 | + |
| 728 | + strbuf_addstr(&path, objdir); |
| 729 | + strbuf_addstr(&path, "/pack"); |
| 730 | + dir = opendir(path.buf); |
| 731 | + if (!dir) { |
| 732 | + if (errno != ENOENT) |
| 733 | + error_errno("unable to open object pack directory: %s", |
| 734 | + path.buf); |
| 735 | + strbuf_release(&path); |
| 736 | + return; |
| 737 | + } |
| 738 | + strbuf_addch(&path, '/'); |
| 739 | + dirnamelen = path.len; |
| 740 | + while ((de = readdir(dir)) != NULL) { |
| 741 | + struct packed_git *p; |
| 742 | + size_t base_len; |
| 743 | + |
| 744 | + if (is_dot_or_dotdot(de->d_name)) |
| 745 | + continue; |
| 746 | + |
| 747 | + strbuf_setlen(&path, dirnamelen); |
| 748 | + strbuf_addstr(&path, de->d_name); |
| 749 | + |
| 750 | + base_len = path.len; |
| 751 | + if (strip_suffix_mem(path.buf, &base_len, ".idx")) { |
| 752 | + /* Don't reopen a pack we already have. */ |
| 753 | + for (p = packed_git; p; p = p->next) { |
| 754 | + size_t len; |
| 755 | + if (strip_suffix(p->pack_name, ".pack", &len) && |
| 756 | + len == base_len && |
| 757 | + !memcmp(p->pack_name, path.buf, len)) |
| 758 | + break; |
| 759 | + } |
| 760 | + if (p == NULL && |
| 761 | + /* |
| 762 | + * See if it really is a valid .idx file with |
| 763 | + * corresponding .pack file that we can map. |
| 764 | + */ |
| 765 | + (p = add_packed_git(path.buf, path.len, local)) != NULL) |
| 766 | + install_packed_git(p); |
| 767 | + } |
| 768 | + |
| 769 | + if (!report_garbage) |
| 770 | + continue; |
| 771 | + |
| 772 | + if (ends_with(de->d_name, ".idx") || |
| 773 | + ends_with(de->d_name, ".pack") || |
| 774 | + ends_with(de->d_name, ".bitmap") || |
| 775 | + ends_with(de->d_name, ".keep")) |
| 776 | + string_list_append(&garbage, path.buf); |
| 777 | + else |
| 778 | + report_garbage(PACKDIR_FILE_GARBAGE, path.buf); |
| 779 | + } |
| 780 | + closedir(dir); |
| 781 | + report_pack_garbage(&garbage); |
| 782 | + string_list_clear(&garbage, 0); |
| 783 | + strbuf_release(&path); |
| 784 | +} |
| 785 | + |
| 786 | +static int approximate_object_count_valid; |
| 787 | + |
| 788 | +/* |
| 789 | + * Give a fast, rough count of the number of objects in the repository. This |
| 790 | + * ignores loose objects completely. If you have a lot of them, then either |
| 791 | + * you should repack because your performance will be awful, or they are |
| 792 | + * all unreachable objects about to be pruned, in which case they're not really |
| 793 | + * interesting as a measure of repo size in the first place. |
| 794 | + */ |
| 795 | +unsigned long approximate_object_count(void) |
| 796 | +{ |
| 797 | + static unsigned long count; |
| 798 | + if (!approximate_object_count_valid) { |
| 799 | + struct packed_git *p; |
| 800 | + |
| 801 | + prepare_packed_git(); |
| 802 | + count = 0; |
| 803 | + for (p = packed_git; p; p = p->next) { |
| 804 | + if (open_pack_index(p)) |
| 805 | + continue; |
| 806 | + count += p->num_objects; |
| 807 | + } |
| 808 | + } |
| 809 | + return count; |
| 810 | +} |
| 811 | + |
| 812 | +static void *get_next_packed_git(const void *p) |
| 813 | +{ |
| 814 | + return ((const struct packed_git *)p)->next; |
| 815 | +} |
| 816 | + |
| 817 | +static void set_next_packed_git(void *p, void *next) |
| 818 | +{ |
| 819 | + ((struct packed_git *)p)->next = next; |
| 820 | +} |
| 821 | + |
| 822 | +static int sort_pack(const void *a_, const void *b_) |
| 823 | +{ |
| 824 | + const struct packed_git *a = a_; |
| 825 | + const struct packed_git *b = b_; |
| 826 | + int st; |
| 827 | + |
| 828 | + /* |
| 829 | + * Local packs tend to contain objects specific to our |
| 830 | + * variant of the project than remote ones. In addition, |
| 831 | + * remote ones could be on a network mounted filesystem. |
| 832 | + * Favor local ones for these reasons. |
| 833 | + */ |
| 834 | + st = a->pack_local - b->pack_local; |
| 835 | + if (st) |
| 836 | + return -st; |
| 837 | + |
| 838 | + /* |
| 839 | + * Younger packs tend to contain more recent objects, |
| 840 | + * and more recent objects tend to get accessed more |
| 841 | + * often. |
| 842 | + */ |
| 843 | + if (a->mtime < b->mtime) |
| 844 | + return 1; |
| 845 | + else if (a->mtime == b->mtime) |
| 846 | + return 0; |
| 847 | + return -1; |
| 848 | +} |
| 849 | + |
| 850 | +static void rearrange_packed_git(void) |
| 851 | +{ |
| 852 | + packed_git = llist_mergesort(packed_git, get_next_packed_git, |
| 853 | + set_next_packed_git, sort_pack); |
| 854 | +} |
| 855 | + |
| 856 | +static void prepare_packed_git_mru(void) |
| 857 | +{ |
| 858 | + struct packed_git *p; |
| 859 | + |
| 860 | + mru_clear(packed_git_mru); |
| 861 | + for (p = packed_git; p; p = p->next) |
| 862 | + mru_append(packed_git_mru, p); |
| 863 | +} |
| 864 | + |
| 865 | +static int prepare_packed_git_run_once = 0; |
| 866 | +void prepare_packed_git(void) |
| 867 | +{ |
| 868 | + struct alternate_object_database *alt; |
| 869 | + |
| 870 | + if (prepare_packed_git_run_once) |
| 871 | + return; |
| 872 | + prepare_packed_git_one(get_object_directory(), 1); |
| 873 | + prepare_alt_odb(); |
| 874 | + for (alt = alt_odb_list; alt; alt = alt->next) |
| 875 | + prepare_packed_git_one(alt->path, 0); |
| 876 | + rearrange_packed_git(); |
| 877 | + prepare_packed_git_mru(); |
| 878 | + prepare_packed_git_run_once = 1; |
| 879 | +} |
| 880 | + |
| 881 | +void reprepare_packed_git(void) |
| 882 | +{ |
| 883 | + approximate_object_count_valid = 0; |
| 884 | + prepare_packed_git_run_once = 0; |
| 885 | + prepare_packed_git(); |
| 886 | +} |
0 commit comments