| #include "git-compat-util.h" |
| #include "dir.h" |
| #include "midx.h" |
| #include "odb.h" |
| #include "packfile.h" |
| #include "path.h" |
| #include "repack.h" |
| #include "repository.h" |
| #include "run-command.h" |
| #include "tempfile.h" |
| |
| void prepare_pack_objects(struct child_process *cmd, |
| const struct pack_objects_args *args, |
| const char *out) |
| { |
| strvec_push(&cmd->args, "pack-objects"); |
| if (args->window) |
| strvec_pushf(&cmd->args, "--window=%s", args->window); |
| if (args->window_memory) |
| strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory); |
| if (args->depth) |
| strvec_pushf(&cmd->args, "--depth=%s", args->depth); |
| if (args->threads) |
| strvec_pushf(&cmd->args, "--threads=%s", args->threads); |
| if (args->max_pack_size) |
| strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size); |
| if (args->no_reuse_delta) |
| strvec_pushf(&cmd->args, "--no-reuse-delta"); |
| if (args->no_reuse_object) |
| strvec_pushf(&cmd->args, "--no-reuse-object"); |
| if (args->name_hash_version) |
| strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version); |
| if (args->path_walk) |
| strvec_pushf(&cmd->args, "--path-walk"); |
| if (args->local) |
| strvec_push(&cmd->args, "--local"); |
| if (args->quiet) |
| strvec_push(&cmd->args, "--quiet"); |
| if (args->delta_base_offset) |
| strvec_push(&cmd->args, "--delta-base-offset"); |
| if (!args->pack_kept_objects) |
| strvec_push(&cmd->args, "--honor-pack-keep"); |
| strvec_push(&cmd->args, out); |
| cmd->git_cmd = 1; |
| cmd->out = -1; |
| } |
| |
| void pack_objects_args_release(struct pack_objects_args *args) |
| { |
| free(args->window); |
| free(args->window_memory); |
| free(args->depth); |
| free(args->threads); |
| list_objects_filter_release(&args->filter_options); |
| } |
| |
| void repack_remove_redundant_pack(struct repository *repo, const char *dir_name, |
| const char *base_name) |
| { |
| struct strbuf buf = STRBUF_INIT; |
| struct odb_source *source = repo->objects->sources; |
| struct multi_pack_index *m = get_multi_pack_index(source); |
| strbuf_addf(&buf, "%s.pack", base_name); |
| if (m && source->local && midx_contains_pack(m, buf.buf)) |
| clear_midx_file(repo); |
| strbuf_insertf(&buf, 0, "%s/", dir_name); |
| unlink_pack_path(buf.buf, 1); |
| strbuf_release(&buf); |
| } |
| |
| const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts) |
| { |
| const char *pack_prefix; |
| if (!skip_prefix(opts->packtmp, opts->packdir, &pack_prefix)) |
| die(_("pack prefix %s does not begin with objdir %s"), |
| opts->packtmp, opts->packdir); |
| if (*pack_prefix == '/') |
| pack_prefix++; |
| return pack_prefix; |
| } |
| |
| bool write_pack_opts_is_local(const struct write_pack_opts *opts) |
| { |
| return starts_with(opts->destination, opts->packdir); |
| } |
| |
| int finish_pack_objects_cmd(const struct git_hash_algo *algop, |
| const struct write_pack_opts *opts, |
| struct child_process *cmd, |
| struct string_list *names) |
| { |
| FILE *out; |
| bool local = write_pack_opts_is_local(opts); |
| struct strbuf line = STRBUF_INIT; |
| |
| out = xfdopen(cmd->out, "r"); |
| while (strbuf_getline_lf(&line, out) != EOF) { |
| struct string_list_item *item; |
| |
| if (line.len != algop->hexsz) |
| die(_("repack: Expecting full hex object ID lines only " |
| "from pack-objects.")); |
| /* |
| * Avoid putting packs written outside of the repository in the |
| * list of names. |
| */ |
| if (local) { |
| item = string_list_append(names, line.buf); |
| item->util = generated_pack_populate(line.buf, |
| opts->packtmp); |
| } |
| } |
| fclose(out); |
| |
| strbuf_release(&line); |
| |
| return finish_command(cmd); |
| } |
| |
| #define DELETE_PACK 1 |
| #define RETAIN_PACK 2 |
| |
| void existing_packs_collect(struct existing_packs *existing, |
| const struct string_list *extra_keep) |
| { |
| struct packfile_store *packs = existing->repo->objects->packfiles; |
| struct packed_git *p; |
| struct strbuf buf = STRBUF_INIT; |
| |
| for (p = packfile_store_get_all_packs(packs); p; p = p->next) { |
| size_t i; |
| const char *base; |
| |
| if (p->multi_pack_index) |
| string_list_append(&existing->midx_packs, |
| pack_basename(p)); |
| if (!p->pack_local) |
| continue; |
| |
| base = pack_basename(p); |
| |
| for (i = 0; i < extra_keep->nr; i++) |
| if (!fspathcmp(base, extra_keep->items[i].string)) |
| break; |
| |
| strbuf_reset(&buf); |
| strbuf_addstr(&buf, base); |
| strbuf_strip_suffix(&buf, ".pack"); |
| |
| if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) |
| string_list_append(&existing->kept_packs, buf.buf); |
| else if (p->is_cruft) |
| string_list_append(&existing->cruft_packs, buf.buf); |
| else |
| string_list_append(&existing->non_kept_packs, buf.buf); |
| } |
| |
| string_list_sort(&existing->kept_packs); |
| string_list_sort(&existing->non_kept_packs); |
| string_list_sort(&existing->cruft_packs); |
| string_list_sort(&existing->midx_packs); |
| strbuf_release(&buf); |
| } |
| |
| int existing_packs_has_non_kept(const struct existing_packs *existing) |
| { |
| return existing->non_kept_packs.nr || existing->cruft_packs.nr; |
| } |
| |
| static void existing_pack_mark_for_deletion(struct string_list_item *item) |
| { |
| item->util = (void*)((uintptr_t)item->util | DELETE_PACK); |
| } |
| |
| static void existing_pack_unmark_for_deletion(struct string_list_item *item) |
| { |
| item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK); |
| } |
| |
| int existing_pack_is_marked_for_deletion(struct string_list_item *item) |
| { |
| return (uintptr_t)item->util & DELETE_PACK; |
| } |
| |
| static void existing_packs_mark_retained(struct string_list_item *item) |
| { |
| item->util = (void*)((uintptr_t)item->util | RETAIN_PACK); |
| } |
| |
| static int existing_pack_is_retained(struct string_list_item *item) |
| { |
| return (uintptr_t)item->util & RETAIN_PACK; |
| } |
| |
| static void existing_packs_mark_for_deletion_1(const struct git_hash_algo *algop, |
| struct string_list *names, |
| struct string_list *list) |
| { |
| struct string_list_item *item; |
| const size_t hexsz = algop->hexsz; |
| |
| for_each_string_list_item(item, list) { |
| char *sha1; |
| size_t len = strlen(item->string); |
| if (len < hexsz) |
| continue; |
| sha1 = item->string + len - hexsz; |
| |
| if (existing_pack_is_retained(item)) { |
| existing_pack_unmark_for_deletion(item); |
| } else if (!string_list_has_string(names, sha1)) { |
| /* |
| * Mark this pack for deletion, which ensures |
| * that this pack won't be included in a MIDX |
| * (if `--write-midx` was given) and that we |
| * will actually delete this pack (if `-d` was |
| * given). |
| */ |
| existing_pack_mark_for_deletion(item); |
| } |
| } |
| } |
| |
| void existing_packs_retain_cruft(struct existing_packs *existing, |
| struct packed_git *cruft) |
| { |
| struct strbuf buf = STRBUF_INIT; |
| struct string_list_item *item; |
| |
| strbuf_addstr(&buf, pack_basename(cruft)); |
| strbuf_strip_suffix(&buf, ".pack"); |
| |
| item = string_list_lookup(&existing->cruft_packs, buf.buf); |
| if (!item) |
| BUG("could not find cruft pack '%s'", pack_basename(cruft)); |
| |
| existing_packs_mark_retained(item); |
| strbuf_release(&buf); |
| } |
| |
| void existing_packs_mark_for_deletion(struct existing_packs *existing, |
| struct string_list *names) |
| |
| { |
| const struct git_hash_algo *algop = existing->repo->hash_algo; |
| existing_packs_mark_for_deletion_1(algop, names, |
| &existing->non_kept_packs); |
| existing_packs_mark_for_deletion_1(algop, names, |
| &existing->cruft_packs); |
| } |
| |
| static void remove_redundant_packs_1(struct repository *repo, |
| struct string_list *packs, |
| const char *packdir) |
| { |
| struct string_list_item *item; |
| for_each_string_list_item(item, packs) { |
| if (!existing_pack_is_marked_for_deletion(item)) |
| continue; |
| repack_remove_redundant_pack(repo, packdir, item->string); |
| } |
| } |
| |
| void existing_packs_remove_redundant(struct existing_packs *existing, |
| const char *packdir) |
| { |
| remove_redundant_packs_1(existing->repo, &existing->non_kept_packs, |
| packdir); |
| remove_redundant_packs_1(existing->repo, &existing->cruft_packs, |
| packdir); |
| } |
| |
| void existing_packs_release(struct existing_packs *existing) |
| { |
| string_list_clear(&existing->kept_packs, 0); |
| string_list_clear(&existing->non_kept_packs, 0); |
| string_list_clear(&existing->cruft_packs, 0); |
| string_list_clear(&existing->midx_packs, 0); |
| } |
| |
| static struct { |
| const char *name; |
| unsigned optional:1; |
| } exts[] = { |
| {".pack"}, |
| {".rev", 1}, |
| {".mtimes", 1}, |
| {".bitmap", 1}, |
| {".promisor", 1}, |
| {".idx"}, |
| }; |
| |
| struct generated_pack { |
| struct tempfile *tempfiles[ARRAY_SIZE(exts)]; |
| }; |
| |
| struct generated_pack *generated_pack_populate(const char *name, |
| const char *packtmp) |
| { |
| struct stat statbuf; |
| struct strbuf path = STRBUF_INIT; |
| struct generated_pack *pack = xcalloc(1, sizeof(*pack)); |
| size_t i; |
| |
| for (i = 0; i < ARRAY_SIZE(exts); i++) { |
| strbuf_reset(&path); |
| strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name); |
| |
| if (stat(path.buf, &statbuf)) |
| continue; |
| |
| pack->tempfiles[i] = register_tempfile(path.buf); |
| } |
| |
| strbuf_release(&path); |
| return pack; |
| } |
| |
| int generated_pack_has_ext(const struct generated_pack *pack, const char *ext) |
| { |
| size_t i; |
| for (i = 0; i < ARRAY_SIZE(exts); i++) { |
| if (strcmp(exts[i].name, ext)) |
| continue; |
| return !!pack->tempfiles[i]; |
| } |
| BUG("unknown pack extension: '%s'", ext); |
| } |
| |
| void generated_pack_install(struct generated_pack *pack, const char *name, |
| const char *packdir, const char *packtmp) |
| { |
| size_t ext; |
| for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { |
| char *fname; |
| |
| fname = mkpathdup("%s/pack-%s%s", packdir, name, |
| exts[ext].name); |
| |
| if (pack->tempfiles[ext]) { |
| const char *fname_old = get_tempfile_path(pack->tempfiles[ext]); |
| struct stat statbuffer; |
| |
| if (!stat(fname_old, &statbuffer)) { |
| statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); |
| chmod(fname_old, statbuffer.st_mode); |
| } |
| |
| if (rename_tempfile(&pack->tempfiles[ext], fname)) |
| die_errno(_("renaming pack to '%s' failed"), |
| fname); |
| } else if (!exts[ext].optional) |
| die(_("pack-objects did not write a '%s' file for pack %s-%s"), |
| exts[ext].name, packtmp, name); |
| else if (unlink(fname) < 0 && errno != ENOENT) |
| die_errno(_("could not unlink: %s"), fname); |
| |
| free(fname); |
| } |
| } |