#include "git-compat-util.h" #include "repack.h" #include "repository.h" #include "run-command.h" #include "string-list.h" #include "hex.h" #include "packfile.h" #include "list-objects-filter-options.h" #include "list-objects-filter.h" #include "odb.h" #include "promisor-remote.h" int write_filtered_pack(const struct write_pack_opts *opts, struct existing_packs *existing, struct string_list *names) { struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; FILE *in; int ret; const char *caret; const char *pack_prefix = write_pack_opts_pack_prefix(opts); prepare_pack_objects(&cmd, opts->po_args, opts->destination); strvec_push(&cmd.args, "--stdin-packs"); for_each_string_list_item(item, &existing->kept_packs) strvec_pushf(&cmd.args, "--keep-pack=%s", item->string); cmd.in = -1; ret = start_command(&cmd); if (ret) return ret; /* * Here 'names' contains only the pack(s) that were just * written, which is exactly the packs we want to keep. Also * 'existing_kept_packs' already contains the packs in * 'keep_pack_list'. */ in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string); for_each_string_list_item(item, &existing->non_kept_packs) fprintf(in, "%s.pack\n", item->string); for_each_string_list_item(item, &existing->cruft_packs) fprintf(in, "%s.pack\n", item->string); caret = opts->po_args->pack_kept_objects ? "" : "^"; for_each_string_list_item(item, &existing->kept_packs) fprintf(in, "%s%s.pack\n", caret, item->string); fclose(in); return finish_pack_objects_cmd(existing->repo->hash_algo, opts, &cmd, names); } struct collect_cb_data { struct repository *repo; struct oidset *set; }; static int collect_promisor_blob(const struct object_id *oid, struct object_info *oi UNUSED, void *cb_data) { struct collect_cb_data *data = cb_data; struct object_info info = OBJECT_INFO_INIT; enum object_type type; info.typep = &type; /* * Use OBJECT_INFO_SKIP_FETCH_OBJECT to avoid triggering a * lazy fetch while collecting promisor blobs. */ if (odb_read_object_info_extended(data->repo->objects, oid, &info, OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) return 0; if (type == OBJ_BLOB) oidset_insert(data->set, oid); return 0; } int enumerate_promisor_blobs(struct repository *repo, const struct list_objects_filter_options *filter, struct oidset *to_drop) { struct oidset all_promisor_blobs = OIDSET_INIT; struct collect_cb_data cb = { .repo = repo, .set = &all_promisor_blobs }; int ret = 0; /* * The caller (cmd_repack) is responsible for validating that a * blob:limit filter and a promisor remote are present before * calling this function. * * Walk only promisor objects. every object visited here is a * promisor object, so it is recoverable from the promisor remote * as long as the remote still has it, the same assumption the rest * of partial clone relies on * We do not use write_filtered_pack() here because git repack * routes promisor objects through repack_promisor_objects() * before the filter machinery runs, so the filtered pack never * contains promisor blobs. Direct enumeration via * ODB_FOR_EACH_OBJECT_PROMISOR_ONLY is the correct approach. */ ret = odb_for_each_object(repo->objects, NULL, collect_promisor_blob, &cb, ODB_FOR_EACH_OBJECT_PROMISOR_ONLY); if (ret) goto cleanup; /* * Apply the filter to find which blobs exceed the threshold. * The caller has to_drop and is responsible for clearing it. */ ret = list_objects_filter__filter_oidset(repo, (struct list_objects_filter_options *)filter, &all_promisor_blobs, to_drop); cleanup: oidset_clear(&all_promisor_blobs); return ret; }