From: Junio C Hamano Date: Thu, 30 Jul 2026 17:44:35 +0000 (-0700) Subject: Merge branch 'tb/repack-geometric-cruft' into seen X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ed3e5a8757e984db6ec4814e6c6149f941a86b04;p=thirdparty%2Fgit.git Merge branch 'tb/repack-geometric-cruft' into seen 'git repack' has been taught to accept '--geometric' and '--cruft' together. When both are given, non-cruft packs are rolled up by the geometric repack as usual, while a separate cruft pack is written to collect unreachable objects. * tb/repack-geometric-cruft: SQUASH??? bare grep !??? repack: support combining '--geometric' with '--cruft' pack-objects: support '--refs-snapshot' with 'follow-reachable' pack-objects: introduce '--stdin-packs=follow-reachable' pack-objects: extract `stdin_packs_add_all_pack_entries()` repack-geometry: drop unused redundant-pack removal repack: delete geometric packs via existing_packs repack: teach MIDX retention about geometric rollups repack: mark geometric progression of packs as retained repack: extract `locate_existing_pack()` helper repack: unconditionally exclude non-kept packs --- ed3e5a8757e984db6ec4814e6c6149f941a86b04 diff --cc builtin/pack-objects.c index 1ec5b6f206,082ff760ab..78c7fd8ad0 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@@ -3950,30 -3937,197 +3954,197 @@@ static int stdin_packs_include_check(st return stdin_packs_include_check_obj((struct object *)commit, data); } - static void stdin_packs_add_pack_entries(struct strmap *packs, - struct rev_info *revs) + /* + * Flag bit set on commits that belong to an included pack during + * '--stdin-packs=follow-reachable'. Used by the pre-walk to + * identify which reachable commits should be tips for the main + * object traversal. + */ + #define IN_INCLUDED_PACK (1u<<11) + + static int mark_included_pack_tip(const struct object_id *oid, + struct packed_git *p, + uint32_t pos, + void *data) { - struct string_list keys = STRING_LIST_INIT_NODUP; + struct rev_info *main_revs = data; + off_t ofs = nth_packed_object_offset(p, pos); + enum object_type type; + struct object_info oi = OBJECT_INFO_INIT; + struct object *obj; + + oi.typep = &type; - if (packed_object_info(p, ofs, &oi) < 0) ++ if (packed_object_info(NULL, p, ofs, &oi) < 0) + return 0; + if (type != OBJ_COMMIT && type != OBJ_TAG) + return 0; + + obj = parse_object(the_repository, oid); + if (!obj) + return 0; + + obj->flags |= IN_INCLUDED_PACK; + + if (type == OBJ_TAG && main_revs) + add_pending_object(main_revs, obj, ""); + return 0; + } + + static int mark_loose_object_tip(const struct object_id *oid, + struct object_info *oi UNUSED, + void *data) + { + struct rev_info *main_revs = data; + struct object *obj; + enum object_type type; + + type = odb_read_object_info(the_repository->objects, oid, NULL); + if (type != OBJ_COMMIT && type != OBJ_TAG) + return 0; + + obj = parse_object(the_repository, oid); + if (!obj) + return 0; + + obj->flags |= IN_INCLUDED_PACK; + + if (type == OBJ_TAG && main_revs) + add_pending_object(main_revs, obj, ""); + + return 0; + } + + static int add_ref_to_pending(const struct reference *ref, void *cb_data) + { + struct rev_info *revs = cb_data; + struct object *object; + + object = parse_object(the_repository, ref->oid); + if (!object) + return 0; + + add_pending_object(revs, object, ""); + return 0; + } + + static void read_refs_snapshot(const char *refs_snapshot, + struct rev_info *revs) + { + struct strbuf buf = STRBUF_INIT; + struct object_id oid; + FILE *f = xfopen(refs_snapshot, "r"); + + while (strbuf_getline(&buf, f) != EOF) { + struct object *object; + const char *hex = buf.buf; + const char *end = NULL; + + if (*hex == '+') + hex++; + + if (parse_oid_hex_algop(hex, &oid, &end, + the_repository->hash_algo) < 0) + die(_("could not parse line: %s"), buf.buf); + if (*end) + die(_("malformed line: %s"), buf.buf); + + object = parse_object(the_repository, &oid); + if (!object) + continue; + + add_pending_object(revs, object, ""); + } + + fclose(f); + strbuf_release(&buf); + } + + static void stdin_packs_add_reachable_pack_entries(struct string_list *keys, + struct rev_info *revs, + int rev_list_unpacked) + { + struct rev_info pre_walk; + struct commit *commit; struct string_list_item *item; - struct hashmap_iter iter; - struct strmap_entry *entry; - strmap_for_each_entry(packs, &iter, entry) { - struct stdin_pack_info *info = entry->value; - if (!info->p) - die(_("could not find pack '%s'"), entry->key); + /* + * Phase 1: mark commits in included packs, then walk from + * ref tips to discover which of them are reachable. The walk + * halts at excluded-closed packs (via no_kept_objects) and + * continues through excluded-open ones. + * + * Also set include_check on the outer revs so that phase 2 + * (the main object traversal) halts at closed packs. + */ + revs->include_check = stdin_packs_include_check; + revs->include_check_obj = stdin_packs_include_check_obj; - string_list_append(&keys, entry->key)->util = info; + for_each_string_list_item(item, keys) { + struct stdin_pack_info *info = item->util; + if (info->kind & STDIN_PACK_INCLUDE) + for_each_object_in_pack(info->p, + mark_included_pack_tip, + revs, + ODB_FOR_EACH_OBJECT_PACK_ORDER); } + if (rev_list_unpacked) { + /* + * With '--stdin-packs=follow-reachable', specifying + * '--unpacked' instructs pack-objects to pack any loose + * objects which are reachable. + * + * Pretend as if all loose objects are in an included + * pack in order to make them eligible for packing. + */ + struct odb_source *source = revs->repo->objects->sources; + for (; source; source = source->next) { + struct odb_source_files *files = odb_source_files_downcast(source); + struct odb_for_each_object_options opts = { 0 }; + if (local) + opts.flags |= ODB_FOR_EACH_OBJECT_LOCAL_ONLY; + + odb_source_for_each_object(&files->loose->base, NULL, + mark_loose_object_tip, + revs, &opts); + } + } + + repo_init_revisions(the_repository, &pre_walk, NULL); + pre_walk.no_kept_objects = 1; + pre_walk.keep_pack_cache_flags |= KEPT_PACK_IN_CORE; + pre_walk.ignore_missing_links = 1; + + if (stdin_packs_refs_snapshot) + read_refs_snapshot(stdin_packs_refs_snapshot, &pre_walk); + else + refs_for_each_ref(get_main_ref_store(the_repository), + add_ref_to_pending, &pre_walk); + + if (prepare_revision_walk(&pre_walk)) + die(_("revision walk setup failed")); + /* - * Order packs by ascending mtime; use QSORT directly to access the - * string_list_item's ->util pointer, which string_list_sort() does not - * provide. + * Phase 2 tips: every reachable commit that is in an + * included pack becomes a starting point for the main + * object traversal. */ - QSORT(keys.items, keys.nr, pack_mtime_cmp); + while ((commit = get_revision(&pre_walk)) != NULL) { + if (commit->object.flags & IN_INCLUDED_PACK) + add_pending_oid(revs, NULL, + &commit->object.oid, 0); + } + + reset_revision_walk(); + release_revisions(&pre_walk); + } + + static void stdin_packs_add_all_pack_entries(struct string_list *keys, + struct rev_info *revs) + { + struct string_list_item *item; - for_each_string_list_item(item, &keys) { + for_each_string_list_item(item, keys) { struct stdin_pack_info *info = item->util; if (info->kind & STDIN_PACK_EXCLUDE_OPEN) {