diff options
author | Jonathan Tan <jonathantanmy@google.com> | 2017-08-18 15:20:26 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2017-08-23 15:12:07 -0700 |
commit | 0abe14f6a5b7681b4ea02d1bd0a64332587ae053 (patch) | |
tree | 0197f856e42a9eda81b1addf3d2ec9effb5c3549 /packfile.c | |
parent | e65f186242ccda24616e40681f46ad8d33da63e2 (diff) | |
download | git-0abe14f6a5b7681b4ea02d1bd0a64332587ae053.tar.gz |
pack: move {,re}prepare_packed_git and approximate_object_count
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'packfile.c')
-rw-r--r-- | packfile.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/packfile.c b/packfile.c index 4eb65e4609..56ec50febe 100644 --- a/packfile.c +++ b/packfile.c @@ -1,6 +1,9 @@ #include "cache.h" #include "mru.h" #include "pack.h" +#include "dir.h" +#include "mergesort.h" +#include "packfile.h" char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, @@ -667,3 +670,217 @@ void install_packed_git(struct packed_git *pack) pack->next = packed_git; packed_git = pack; } + +void (*report_garbage)(unsigned seen_bits, const char *path); + +static void report_helper(const struct string_list *list, + int seen_bits, int first, int last) +{ + if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX)) + return; + + for (; first < last; first++) + report_garbage(seen_bits, list->items[first].string); +} + +static void report_pack_garbage(struct string_list *list) +{ + int i, baselen = -1, first = 0, seen_bits = 0; + + if (!report_garbage) + return; + + string_list_sort(list); + + for (i = 0; i < list->nr; i++) { + const char *path = list->items[i].string; + if (baselen != -1 && + strncmp(path, list->items[first].string, baselen)) { + report_helper(list, seen_bits, first, i); + baselen = -1; + seen_bits = 0; + } + if (baselen == -1) { + const char *dot = strrchr(path, '.'); + if (!dot) { + report_garbage(PACKDIR_FILE_GARBAGE, path); + continue; + } + baselen = dot - path + 1; + first = i; + } + if (!strcmp(path + baselen, "pack")) + seen_bits |= 1; + else if (!strcmp(path + baselen, "idx")) + seen_bits |= 2; + } + report_helper(list, seen_bits, first, list->nr); +} + +static void prepare_packed_git_one(char *objdir, int local) +{ + struct strbuf path = STRBUF_INIT; + size_t dirnamelen; + DIR *dir; + struct dirent *de; + struct string_list garbage = STRING_LIST_INIT_DUP; + + strbuf_addstr(&path, objdir); + strbuf_addstr(&path, "/pack"); + dir = opendir(path.buf); + if (!dir) { + if (errno != ENOENT) + error_errno("unable to open object pack directory: %s", + path.buf); + strbuf_release(&path); + return; + } + strbuf_addch(&path, '/'); + dirnamelen = path.len; + while ((de = readdir(dir)) != NULL) { + struct packed_git *p; + size_t base_len; + + if (is_dot_or_dotdot(de->d_name)) + continue; + + strbuf_setlen(&path, dirnamelen); + strbuf_addstr(&path, de->d_name); + + base_len = path.len; + if (strip_suffix_mem(path.buf, &base_len, ".idx")) { + /* Don't reopen a pack we already have. */ + for (p = packed_git; p; p = p->next) { + size_t len; + if (strip_suffix(p->pack_name, ".pack", &len) && + len == base_len && + !memcmp(p->pack_name, path.buf, len)) + break; + } + if (p == NULL && + /* + * See if it really is a valid .idx file with + * corresponding .pack file that we can map. + */ + (p = add_packed_git(path.buf, path.len, local)) != NULL) + install_packed_git(p); + } + + if (!report_garbage) + continue; + + if (ends_with(de->d_name, ".idx") || + ends_with(de->d_name, ".pack") || + ends_with(de->d_name, ".bitmap") || + ends_with(de->d_name, ".keep")) + string_list_append(&garbage, path.buf); + else + report_garbage(PACKDIR_FILE_GARBAGE, path.buf); + } + closedir(dir); + report_pack_garbage(&garbage); + string_list_clear(&garbage, 0); + strbuf_release(&path); +} + +static int approximate_object_count_valid; + +/* + * Give a fast, rough count of the number of objects in the repository. This + * ignores loose objects completely. If you have a lot of them, then either + * you should repack because your performance will be awful, or they are + * all unreachable objects about to be pruned, in which case they're not really + * interesting as a measure of repo size in the first place. + */ +unsigned long approximate_object_count(void) +{ + static unsigned long count; + if (!approximate_object_count_valid) { + struct packed_git *p; + + prepare_packed_git(); + count = 0; + for (p = packed_git; p; p = p->next) { + if (open_pack_index(p)) + continue; + count += p->num_objects; + } + } + return count; +} + +static void *get_next_packed_git(const void *p) +{ + return ((const struct packed_git *)p)->next; +} + +static void set_next_packed_git(void *p, void *next) +{ + ((struct packed_git *)p)->next = next; +} + +static int sort_pack(const void *a_, const void *b_) +{ + const struct packed_git *a = a_; + const struct packed_git *b = b_; + int st; + + /* + * Local packs tend to contain objects specific to our + * variant of the project than remote ones. In addition, + * remote ones could be on a network mounted filesystem. + * Favor local ones for these reasons. + */ + st = a->pack_local - b->pack_local; + if (st) + return -st; + + /* + * Younger packs tend to contain more recent objects, + * and more recent objects tend to get accessed more + * often. + */ + if (a->mtime < b->mtime) + return 1; + else if (a->mtime == b->mtime) + return 0; + return -1; +} + +static void rearrange_packed_git(void) +{ + packed_git = llist_mergesort(packed_git, get_next_packed_git, + set_next_packed_git, sort_pack); +} + +static void prepare_packed_git_mru(void) +{ + struct packed_git *p; + + mru_clear(packed_git_mru); + for (p = packed_git; p; p = p->next) + mru_append(packed_git_mru, p); +} + +static int prepare_packed_git_run_once = 0; +void prepare_packed_git(void) +{ + struct alternate_object_database *alt; + + if (prepare_packed_git_run_once) + return; + prepare_packed_git_one(get_object_directory(), 1); + prepare_alt_odb(); + for (alt = alt_odb_list; alt; alt = alt->next) + prepare_packed_git_one(alt->path, 0); + rearrange_packed_git(); + prepare_packed_git_mru(); + prepare_packed_git_run_once = 1; +} + +void reprepare_packed_git(void) +{ + approximate_object_count_valid = 0; + prepare_packed_git_run_once = 0; + prepare_packed_git(); +} |