packfile: move the MRU list into the packfile store

Packfiles have two lists associated to them: - A list that keeps track of packfiles in the order that they were added to a packfile store. - A list that keeps track of packfiles in most-recently-used order so that packfiles that are more likely to contain a specific object are ordered towards the front. Both of these lists are hosted by `struct packed_git` itself, So to identify all packfiles in a repository you simply need to grab the first packfile and then iterate the `->next` pointers or the MRU list. This pattern has the problem that all packfiles are part of the same list, regardless of whether or not they belong to the same object source. With the upcoming pluggable object database effort this needs to change: packfiles should be contained by a single object source, and reading an object from any such packfile should use that source to look up the object. Consequently, we need to break up the global lists of packfiles into per-object-source lists. A first step towards this goal is to move those lists out of `struct packed_git` and into the packfile store. While the packfile store is currently sitting on the `struct object_database` level, the intent is to push it down one level into the `struct odb_source` in a subsequent patch series. Introduce a new `struct packfile_list` that is used to manage lists of packfiles and use it to store the list of most-recently-used packfiles in `struct packfile_store`. For now, the new list type is only used in a single spot, but we'll expand its usage in subsequent patches. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Oct 30, 2025 at 11:38 UTC f905a855b1d1e172ba1e51e03fc5ec531445575e
4 files changed +104 -18
builtin/pack-objects.c
+4 -5
@@ -1706,8 +1706,8 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
1706 uint32_t found_mtime)
1707 {
1708 int want;
1709 + struct packfile_list_entry *e;
1710 struct odb_source *source;
1710 - struct list_head *pos;
1711
1712 if (!exclude && local) {
1713 /*
@@ -1748,12 +1748,11 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
1748 }
1749 }
1750
1751 - list_for_each(pos, packfile_store_get_packs_mru(the_repository->objects->packfiles)) {
1752 - struct packed_git *p = list_entry(pos, struct packed_git, mru);
1751 + for (e = the_repository->objects->packfiles->mru.head; e; e = e->next) {
1752 + struct packed_git *p = e->pack;
1753 want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime);
1754 if (!exclude && want > 0)
1755 - list_move(&p->mru,
1756 - packfile_store_get_packs_mru(the_repository->objects->packfiles));
1755 + packfile_list_prepend(&the_repository->objects->packfiles->mru, p);
1756 if (want != -1)
1757 return want;
1758 }
midx.c
+1 -1
@@ -463,7 +463,7 @@ int prepare_midx_pack(struct multi_pack_index *m,
463 p = packfile_store_load_pack(r->objects->packfiles,
464 pack_name.buf, m->source->local);
465 if (p)
466 - list_add_tail(&p->mru, &r->objects->packfiles->mru);
466 + packfile_list_append(&m->source->odb->packfiles->mru, p);
467 strbuf_release(&pack_name);
468
469 if (!p) {
packfile.c
+83 -9
@@ -47,6 +47,80 @@ static size_t pack_mapped;
47 #define SZ_FMT PRIuMAX
48 static inline uintmax_t sz_fmt(size_t s) { return s; }
49
50 +void packfile_list_clear(struct packfile_list *list)
51 +{
52 + struct packfile_list_entry *e, *next;
53 +
54 + for (e = list->head; e; e = next) {
55 + next = e->next;
56 + free(e);
57 + }
58 +
59 + list->head = list->tail = NULL;
60 +}
61 +
62 +static struct packfile_list_entry *packfile_list_remove_internal(struct packfile_list *list,
63 + struct packed_git *pack)
64 +{
65 + struct packfile_list_entry *e, *prev;
66 +
67 + for (e = list->head, prev = NULL; e; prev = e, e = e->next) {
68 + if (e->pack != pack)
69 + continue;
70 +
71 + if (prev)
72 + prev->next = e->next;
73 + if (list->head == e)
74 + list->head = e->next;
75 + if (list->tail == e)
76 + list->tail = prev;
77 +
78 + return e;
79 + }
80 +
81 + return NULL;
82 +}
83 +
84 +void packfile_list_remove(struct packfile_list *list, struct packed_git *pack)
85 +{
86 + free(packfile_list_remove_internal(list, pack));
87 +}
88 +
89 +void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack)
90 +{
91 + struct packfile_list_entry *entry;
92 +
93 + entry = packfile_list_remove_internal(list, pack);
94 + if (!entry) {
95 + entry = xmalloc(sizeof(*entry));
96 + entry->pack = pack;
97 + }
98 + entry->next = list->head;
99 +
100 + list->head = entry;
101 + if (!list->tail)
102 + list->tail = entry;
103 +}
104 +
105 +void packfile_list_append(struct packfile_list *list, struct packed_git *pack)
106 +{
107 + struct packfile_list_entry *entry;
108 +
109 + entry = packfile_list_remove_internal(list, pack);
110 + if (!entry) {
111 + entry = xmalloc(sizeof(*entry));
112 + entry->pack = pack;
113 + }
114 + entry->next = NULL;
115 +
116 + if (list->tail) {
117 + list->tail->next = entry;
118 + list->tail = entry;
119 + } else {
120 + list->head = list->tail = entry;
121 + }
122 +}
123 +
124 void pack_report(struct repository *repo)
125 {
126 fprintf(stderr,
@@ -995,10 +1069,10 @@ static void packfile_store_prepare_mru(struct packfile_store *store)
1069 {
1070 struct packed_git *p;
1071
998 - INIT_LIST_HEAD(&store->mru);
1072 + packfile_list_clear(&store->mru);
1073
1074 for (p = store->packs; p; p = p->next)
1001 - list_add_tail(&p->mru, &store->mru);
1075 + packfile_list_append(&store->mru, p);
1076 }
1077
1078 void packfile_store_prepare(struct packfile_store *store)
@@ -1040,10 +1114,10 @@ struct packed_git *packfile_store_get_packs(struct packfile_store *store)
1114 return store->packs;
1115 }
1116
1043 -struct list_head *packfile_store_get_packs_mru(struct packfile_store *store)
1117 +struct packfile_list_entry *packfile_store_get_packs_mru(struct packfile_store *store)
1118 {
1119 packfile_store_prepare(store);
1046 - return &store->mru;
1120 + return store->mru.head;
1121 }
1122
1123 /*
@@ -2048,7 +2122,7 @@ static int fill_pack_entry(const struct object_id *oid,
2122
2123 int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e)
2124 {
2051 - struct list_head *pos;
2125 + struct packfile_list_entry *l;
2126
2127 packfile_store_prepare(r->objects->packfiles);
2128
@@ -2059,10 +2133,11 @@ int find_pack_entry(struct repository *r, const struct object_id *oid, struct pa
2133 if (!r->objects->packfiles->packs)
2134 return 0;
2135
2062 - list_for_each(pos, &r->objects->packfiles->mru) {
2063 - struct packed_git *p = list_entry(pos, struct packed_git, mru);
2136 + for (l = r->objects->packfiles->mru.head; l; l = l->next) {
2137 + struct packed_git *p = l->pack;
2138 +
2139 if (!p->multi_pack_index && fill_pack_entry(oid, e, p)) {
2065 - list_move(&p->mru, &r->objects->packfiles->mru);
2140 + packfile_list_prepend(&r->objects->packfiles->mru, p);
2141 return 1;
2142 }
2143 }
@@ -2314,7 +2389,6 @@ struct packfile_store *packfile_store_new(struct object_database *odb)
2389 struct packfile_store *store;
2390 CALLOC_ARRAY(store, 1);
2391 store->odb = odb;
2317 - INIT_LIST_HEAD(&store->mru);
2392 strmap_init(&store->packs_by_path);
2393 return store;
2394 }
packfile.h
+16 -3
@@ -12,7 +12,6 @@ struct object_info;
12
13 struct packed_git {
14 struct packed_git *next;
15 - struct list_head mru;
15 struct pack_window *windows;
16 off_t pack_size;
17 const void *index_data;
@@ -52,6 +51,20 @@ struct packed_git {
51 char pack_name[FLEX_ARRAY]; /* more */
52 };
53
54 +struct packfile_list {
55 + struct packfile_list_entry *head, *tail;
56 +};
57 +
58 +struct packfile_list_entry {
59 + struct packfile_list_entry *next;
60 + struct packed_git *pack;
61 +};
62 +
63 +void packfile_list_clear(struct packfile_list *list);
64 +void packfile_list_remove(struct packfile_list *list, struct packed_git *pack);
65 +void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack);
66 +void packfile_list_append(struct packfile_list *list, struct packed_git *pack);
67 +
68 /*
69 * A store that manages packfiles for a given object database.
70 */
@@ -79,7 +92,7 @@ struct packfile_store {
92 } kept_cache;
93
94 /* A most-recently-used ordered version of the packs list. */
82 - struct list_head mru;
95 + struct packfile_list mru;
96
97 /*
98 * A map of packfile names to packed_git structs for tracking which
@@ -153,7 +166,7 @@ struct packed_git *packfile_store_get_packs(struct packfile_store *store);
166 /*
167 * Get all packs in most-recently-used order.
168 */
156 -struct list_head *packfile_store_get_packs_mru(struct packfile_store *store);
169 +struct packfile_list_entry *packfile_store_get_packs_mru(struct packfile_store *store);
170
171 /*
172 * Open the packfile and add it to the store if it isn't yet known. Returns