]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'tb/repack-geometric-cruft' into seen
authorJunio C Hamano <gitster@pobox.com>
Thu, 30 Jul 2026 17:44:35 +0000 (10:44 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 30 Jul 2026 17:44:35 +0000 (10:44 -0700)
'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

1  2 
Documentation/git-pack-objects.adoc
builtin/pack-objects.c
builtin/repack.c
repack-geometry.c
repack.c
t/t7704-repack-cruft.sh

Simple merge
index 1ec5b6f206366eafd1f8e421752e13d2c1122b7c,082ff760abc6ca9f4f10b61e28d103f9c3d563b9..78c7fd8ad03562b2fe6335b156dd343268f42737
@@@ -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) {
Simple merge
Simple merge
diff --cc repack.c
Simple merge
Simple merge