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) {