odb/source-packed: wire up `for_each_object()` callback

Move `packfile_store_for_each_object()` and its associated helpers from "packfile.c" into "odb/source-packed.c" and wire it up as the `for_each_object()` callback of the "packed" source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 17, 2026 at 08:39 UTC 7ed53cde288a1e5558acac33f2d89f17b81618e2
7 files changed +269 -280
builtin/cat-file.c
+2 -2
@@ -916,8 +916,8 @@ static void batch_each_object(struct batch_options *opt,
916
917 for (source = the_repository->objects->sources; source; source = source->next) {
918 struct odb_source_files *files = odb_source_files_downcast(source);
919 - int ret = packfile_store_for_each_object(files->packed, &oi,
920 - batch_one_object_oi, &payload, &opts);
919 + int ret = odb_source_for_each_object(&files->packed->base, &oi,
920 + batch_one_object_oi, &payload, &opts);
921 if (ret)
922 break;
923 }
builtin/pack-objects.c
+2 -2
@@ -4503,8 +4503,8 @@ static void add_objects_in_unpacked_packs(void)
4503 if (!source->local)
4504 continue;
4505
4506 - if (packfile_store_for_each_object(files->packed, &oi,
4507 - add_object_in_unpacked_pack, NULL, &opts))
4506 + if (odb_source_for_each_object(&files->packed->base, &oi,
4507 + add_object_in_unpacked_pack, NULL, &opts))
4508 die(_("cannot open pack index"));
4509 }
4510 }
commit-graph.c
+2 -2
@@ -2016,8 +2016,8 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
2016 odb_prepare_alternates(ctx->r->objects);
2017 for (source = ctx->r->objects->sources; source; source = source->next) {
2018 struct odb_source_files *files = odb_source_files_downcast(source);
2019 - packfile_store_for_each_object(files->packed, &oi, add_packed_commits_oi,
2020 - ctx, &opts);
2019 + odb_source_for_each_object(&files->packed->base, &oi, add_packed_commits_oi,
2020 + ctx, &opts);
2021 }
2022
2023 if (ctx->progress_done < ctx->approx_nr_objects)
odb/source-files.c
+1 -1
@@ -88,7 +88,7 @@ static int odb_source_files_for_each_object(struct odb_source *source,
88 return ret;
89 }
90
91 - ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, opts);
91 + ret = odb_source_for_each_object(&files->packed->base, request, cb, cb_data, opts);
92 if (ret)
93 return ret;
94
odb/source-packed.c
+258
@@ -81,6 +81,263 @@ static int odb_source_packed_read_object_stream(struct odb_read_stream **out,
81 return packfile_read_object_stream(out, oid, e.p, e.offset);
82 }
83
84 +struct odb_source_packed_for_each_object_wrapper_data {
85 + struct odb_source_packed *store;
86 + const struct object_info *request;
87 + odb_for_each_object_cb cb;
88 + void *cb_data;
89 +};
90 +
91 +static int odb_source_packed_for_each_object_wrapper(const struct object_id *oid,
92 + struct packed_git *pack,
93 + uint32_t index_pos,
94 + void *cb_data)
95 +{
96 + struct odb_source_packed_for_each_object_wrapper_data *data = cb_data;
97 +
98 + if (data->request) {
99 + off_t offset = nth_packed_object_offset(pack, index_pos);
100 + struct object_info oi = *data->request;
101 +
102 + if (packed_object_info_with_index_pos(pack, offset,
103 + &index_pos, &oi) < 0) {
104 + mark_bad_packed_object(pack, oid);
105 + return -1;
106 + }
107 +
108 + return data->cb(oid, &oi, data->cb_data);
109 + } else {
110 + return data->cb(oid, NULL, data->cb_data);
111 + }
112 +}
113 +
114 +static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
115 +{
116 + do {
117 + if (*a != *b)
118 + return 0;
119 + a++;
120 + b++;
121 + len -= 2;
122 + } while (len > 1);
123 + if (len)
124 + if ((*a ^ *b) & 0xf0)
125 + return 0;
126 + return 1;
127 +}
128 +
129 +static int for_each_prefixed_object_in_midx(
130 + struct odb_source_packed *store,
131 + struct multi_pack_index *m,
132 + const struct odb_for_each_object_options *opts,
133 + struct odb_source_packed_for_each_object_wrapper_data *data)
134 +{
135 + int ret;
136 +
137 + for (; m; m = m->base_midx) {
138 + uint32_t num, i, first = 0;
139 + int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ?
140 + m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len;
141 +
142 + if (!m->num_objects)
143 + continue;
144 +
145 + num = m->num_objects + m->num_objects_in_base;
146 +
147 + bsearch_one_midx(opts->prefix, m, &first);
148 +
149 + /*
150 + * At this point, "first" is the location of the lowest
151 + * object with an object name that could match "opts->prefix".
152 + * See if we have 0, 1 or more objects that actually match(es).
153 + */
154 + for (i = first; i < num; i++) {
155 + const struct object_id *current = NULL;
156 + struct object_id oid;
157 +
158 + current = nth_midxed_object_oid(&oid, m, i);
159 +
160 + if (!match_hash(len, opts->prefix->hash, current->hash))
161 + break;
162 +
163 + if (data->request) {
164 + struct object_info oi = *data->request;
165 +
166 + ret = odb_source_read_object_info(&store->base, current,
167 + &oi, 0);
168 + if (ret)
169 + goto out;
170 +
171 + ret = data->cb(&oid, &oi, data->cb_data);
172 + if (ret)
173 + goto out;
174 + } else {
175 + ret = data->cb(&oid, NULL, data->cb_data);
176 + if (ret)
177 + goto out;
178 + }
179 + }
180 + }
181 +
182 + ret = 0;
183 +
184 +out:
185 + return ret;
186 +}
187 +
188 +static int for_each_prefixed_object_in_pack(
189 + struct odb_source_packed *store,
190 + struct packed_git *p,
191 + const struct odb_for_each_object_options *opts,
192 + struct odb_source_packed_for_each_object_wrapper_data *data)
193 +{
194 + uint32_t num, i, first = 0;
195 + int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ?
196 + p->repo->hash_algo->hexsz : opts->prefix_hex_len;
197 + int ret;
198 +
199 + num = p->num_objects;
200 + bsearch_pack(opts->prefix, p, &first);
201 +
202 + /*
203 + * At this point, "first" is the location of the lowest object
204 + * with an object name that could match "bin_pfx". See if we have
205 + * 0, 1 or more objects that actually match(es).
206 + */
207 + for (i = first; i < num; i++) {
208 + struct object_id oid;
209 +
210 + nth_packed_object_id(&oid, p, i);
211 + if (!match_hash(len, opts->prefix->hash, oid.hash))
212 + break;
213 +
214 + if (data->request) {
215 + struct object_info oi = *data->request;
216 +
217 + ret = odb_source_read_object_info(&store->base, &oid, &oi, 0);
218 + if (ret)
219 + goto out;
220 +
221 + ret = data->cb(&oid, &oi, data->cb_data);
222 + if (ret)
223 + goto out;
224 + } else {
225 + ret = data->cb(&oid, NULL, data->cb_data);
226 + if (ret)
227 + goto out;
228 + }
229 + }
230 +
231 + ret = 0;
232 +
233 +out:
234 + return ret;
235 +}
236 +
237 +static int odb_source_packed_for_each_prefixed_object(
238 + struct odb_source_packed *store,
239 + const struct odb_for_each_object_options *opts,
240 + struct odb_source_packed_for_each_object_wrapper_data *data)
241 +{
242 + struct packfile_list_entry *e;
243 + struct multi_pack_index *m;
244 + bool pack_errors = false;
245 + int ret;
246 +
247 + if (opts->flags)
248 + BUG("flags unsupported");
249 +
250 + store->skip_mru_updates = true;
251 +
252 + m = get_multi_pack_index(&store->files->base);
253 + if (m) {
254 + ret = for_each_prefixed_object_in_midx(store, m, opts, data);
255 + if (ret)
256 + goto out;
257 + }
258 +
259 + for (e = packfile_store_get_packs(store); e; e = e->next) {
260 + if (e->pack->multi_pack_index)
261 + continue;
262 +
263 + if (open_pack_index(e->pack)) {
264 + pack_errors = true;
265 + continue;
266 + }
267 +
268 + if (!e->pack->num_objects)
269 + continue;
270 +
271 + ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data);
272 + if (ret)
273 + goto out;
274 + }
275 +
276 + ret = 0;
277 +
278 +out:
279 + store->skip_mru_updates = false;
280 + if (!ret && pack_errors)
281 + ret = -1;
282 + return ret;
283 +}
284 +
285 +static int odb_source_packed_for_each_object(struct odb_source *source,
286 + const struct object_info *request,
287 + odb_for_each_object_cb cb,
288 + void *cb_data,
289 + const struct odb_for_each_object_options *opts)
290 +{
291 + struct odb_source_packed *packed = odb_source_packed_downcast(source);
292 + struct odb_source_packed_for_each_object_wrapper_data data = {
293 + .store = packed,
294 + .request = request,
295 + .cb = cb,
296 + .cb_data = cb_data,
297 + };
298 + struct packfile_list_entry *e;
299 + int pack_errors = 0, ret;
300 +
301 + if (opts->prefix)
302 + return odb_source_packed_for_each_prefixed_object(packed, opts, &data);
303 +
304 + packed->skip_mru_updates = true;
305 +
306 + for (e = packfile_store_get_packs(packed); e; e = e->next) {
307 + struct packed_git *p = e->pack;
308 +
309 + if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
310 + continue;
311 + if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
312 + !p->pack_promisor)
313 + continue;
314 + if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
315 + p->pack_keep_in_core)
316 + continue;
317 + if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
318 + p->pack_keep)
319 + continue;
320 + if (open_pack_index(p)) {
321 + pack_errors = 1;
322 + continue;
323 + }
324 +
325 + ret = for_each_object_in_pack(p, odb_source_packed_for_each_object_wrapper,
326 + &data, opts->flags);
327 + if (ret)
328 + goto out;
329 + }
330 +
331 + ret = 0;
332 +
333 +out:
334 + packed->skip_mru_updates = false;
335 +
336 + if (!ret && pack_errors)
337 + ret = -1;
338 + return ret;
339 +}
340 +
341 void (*report_garbage)(unsigned seen_bits, const char *path);
342
343 static void report_helper(const struct string_list *list,
@@ -291,6 +548,7 @@ struct odb_source_packed *odb_source_packed_new(struct odb_source_files *parent)
548 packed->base.reprepare = odb_source_packed_reprepare;
549 packed->base.read_object_info = odb_source_packed_read_object_info;
550 packed->base.read_object_stream = odb_source_packed_read_object_stream;
551 + packed->base.for_each_object = odb_source_packed_for_each_object;
552
553 if (!is_absolute_path(parent->base.path))
554 chdir_notify_register(NULL, odb_source_packed_reparent, packed);
packfile.c
+2 -258
@@ -1362,8 +1362,8 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
1362 hashmap_add(&delta_base_cache, &ent->ent);
1363 }
1364
1365 -static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
1366 - uint32_t *maybe_index_pos, struct object_info *oi)
1365 +int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
1366 + uint32_t *maybe_index_pos, struct object_info *oi)
1367 {
1368 struct pack_window *w_curs = NULL;
1369 size_t size;
@@ -2068,262 +2068,6 @@ int for_each_object_in_pack(struct packed_git *p,
2068 return r;
2069 }
2070
2071 -struct odb_source_packed_for_each_object_wrapper_data {
2072 - struct odb_source_packed *store;
2073 - const struct object_info *request;
2074 - odb_for_each_object_cb cb;
2075 - void *cb_data;
2076 -};
2077 -
2078 -static int packfile_store_for_each_object_wrapper(const struct object_id *oid,
2079 - struct packed_git *pack,
2080 - uint32_t index_pos,
2081 - void *cb_data)
2082 -{
2083 - struct odb_source_packed_for_each_object_wrapper_data *data = cb_data;
2084 -
2085 - if (data->request) {
2086 - off_t offset = nth_packed_object_offset(pack, index_pos);
2087 - struct object_info oi = *data->request;
2088 -
2089 - if (packed_object_info_with_index_pos(pack, offset,
2090 - &index_pos, &oi) < 0) {
2091 - mark_bad_packed_object(pack, oid);
2092 - return -1;
2093 - }
2094 -
2095 - return data->cb(oid, &oi, data->cb_data);
2096 - } else {
2097 - return data->cb(oid, NULL, data->cb_data);
2098 - }
2099 -}
2100 -
2101 -static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
2102 -{
2103 - do {
2104 - if (*a != *b)
2105 - return 0;
2106 - a++;
2107 - b++;
2108 - len -= 2;
2109 - } while (len > 1);
2110 - if (len)
2111 - if ((*a ^ *b) & 0xf0)
2112 - return 0;
2113 - return 1;
2114 -}
2115 -
2116 -static int for_each_prefixed_object_in_midx(
2117 - struct odb_source_packed *store,
2118 - struct multi_pack_index *m,
2119 - const struct odb_for_each_object_options *opts,
2120 - struct odb_source_packed_for_each_object_wrapper_data *data)
2121 -{
2122 - int ret;
2123 -
2124 - for (; m; m = m->base_midx) {
2125 - uint32_t num, i, first = 0;
2126 - int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ?
2127 - m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len;
2128 -
2129 - if (!m->num_objects)
2130 - continue;
2131 -
2132 - num = m->num_objects + m->num_objects_in_base;
2133 -
2134 - bsearch_one_midx(opts->prefix, m, &first);
2135 -
2136 - /*
2137 - * At this point, "first" is the location of the lowest
2138 - * object with an object name that could match "opts->prefix".
2139 - * See if we have 0, 1 or more objects that actually match(es).
2140 - */
2141 - for (i = first; i < num; i++) {
2142 - const struct object_id *current = NULL;
2143 - struct object_id oid;
2144 -
2145 - current = nth_midxed_object_oid(&oid, m, i);
2146 -
2147 - if (!match_hash(len, opts->prefix->hash, current->hash))
2148 - break;
2149 -
2150 - if (data->request) {
2151 - struct object_info oi = *data->request;
2152 -
2153 - ret = odb_source_read_object_info(&store->base, current,
2154 - &oi, 0);
2155 - if (ret)
2156 - goto out;
2157 -
2158 - ret = data->cb(&oid, &oi, data->cb_data);
2159 - if (ret)
2160 - goto out;
2161 - } else {
2162 - ret = data->cb(&oid, NULL, data->cb_data);
2163 - if (ret)
2164 - goto out;
2165 - }
2166 - }
2167 - }
2168 -
2169 - ret = 0;
2170 -
2171 -out:
2172 - return ret;
2173 -}
2174 -
2175 -static int for_each_prefixed_object_in_pack(
2176 - struct odb_source_packed *store,
2177 - struct packed_git *p,
2178 - const struct odb_for_each_object_options *opts,
2179 - struct odb_source_packed_for_each_object_wrapper_data *data)
2180 -{
2181 - uint32_t num, i, first = 0;
2182 - int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ?
2183 - p->repo->hash_algo->hexsz : opts->prefix_hex_len;
2184 - int ret;
2185 -
2186 - num = p->num_objects;
2187 - bsearch_pack(opts->prefix, p, &first);
2188 -
2189 - /*
2190 - * At this point, "first" is the location of the lowest object
2191 - * with an object name that could match "bin_pfx". See if we have
2192 - * 0, 1 or more objects that actually match(es).
2193 - */
2194 - for (i = first; i < num; i++) {
2195 - struct object_id oid;
2196 -
2197 - nth_packed_object_id(&oid, p, i);
2198 - if (!match_hash(len, opts->prefix->hash, oid.hash))
2199 - break;
2200 -
2201 - if (data->request) {
2202 - struct object_info oi = *data->request;
2203 -
2204 - ret = odb_source_read_object_info(&store->base, &oid, &oi, 0);
2205 - if (ret)
2206 - goto out;
2207 -
2208 - ret = data->cb(&oid, &oi, data->cb_data);
2209 - if (ret)
2210 - goto out;
2211 - } else {
2212 - ret = data->cb(&oid, NULL, data->cb_data);
2213 - if (ret)
2214 - goto out;
2215 - }
2216 - }
2217 -
2218 - ret = 0;
2219 -
2220 -out:
2221 - return ret;
2222 -}
2223 -
2224 -static int packfile_store_for_each_prefixed_object(
2225 - struct odb_source_packed *store,
2226 - const struct odb_for_each_object_options *opts,
2227 - struct odb_source_packed_for_each_object_wrapper_data *data)
2228 -{
2229 - struct packfile_list_entry *e;
2230 - struct multi_pack_index *m;
2231 - bool pack_errors = false;
2232 - int ret;
2233 -
2234 - if (opts->flags)
2235 - BUG("flags unsupported");
2236 -
2237 - store->skip_mru_updates = true;
2238 -
2239 - m = get_multi_pack_index(&store->files->base);
2240 - if (m) {
2241 - ret = for_each_prefixed_object_in_midx(store, m, opts, data);
2242 - if (ret)
2243 - goto out;
2244 - }
2245 -
2246 - for (e = packfile_store_get_packs(store); e; e = e->next) {
2247 - if (e->pack->multi_pack_index)
2248 - continue;
2249 -
2250 - if (open_pack_index(e->pack)) {
2251 - pack_errors = true;
2252 - continue;
2253 - }
2254 -
2255 - if (!e->pack->num_objects)
2256 - continue;
2257 -
2258 - ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data);
2259 - if (ret)
2260 - goto out;
2261 - }
2262 -
2263 - ret = 0;
2264 -
2265 -out:
2266 - store->skip_mru_updates = false;
2267 - if (!ret && pack_errors)
2268 - ret = -1;
2269 - return ret;
2270 -}
2271 -
2272 -int packfile_store_for_each_object(struct odb_source_packed *store,
2273 - const struct object_info *request,
2274 - odb_for_each_object_cb cb,
2275 - void *cb_data,
2276 - const struct odb_for_each_object_options *opts)
2277 -{
2278 - struct odb_source_packed_for_each_object_wrapper_data data = {
2279 - .store = store,
2280 - .request = request,
2281 - .cb = cb,
2282 - .cb_data = cb_data,
2283 - };
2284 - struct packfile_list_entry *e;
2285 - int pack_errors = 0, ret;
2286 -
2287 - if (opts->prefix)
2288 - return packfile_store_for_each_prefixed_object(store, opts, &data);
2289 -
2290 - store->skip_mru_updates = true;
2291 -
2292 - for (e = packfile_store_get_packs(store); e; e = e->next) {
2293 - struct packed_git *p = e->pack;
2294 -
2295 - if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
2296 - continue;
2297 - if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
2298 - !p->pack_promisor)
2299 - continue;
2300 - if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
2301 - p->pack_keep_in_core)
2302 - continue;
2303 - if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
2304 - p->pack_keep)
2305 - continue;
2306 - if (open_pack_index(p)) {
2307 - pack_errors = 1;
2308 - continue;
2309 - }
2310 -
2311 - ret = for_each_object_in_pack(p, packfile_store_for_each_object_wrapper,
2312 - &data, opts->flags);
2313 - if (ret)
2314 - goto out;
2315 - }
2316 -
2317 - ret = 0;
2318 -
2319 -out:
2320 - store->skip_mru_updates = false;
2321 -
2322 - if (!ret && pack_errors)
2323 - ret = -1;
2324 - return ret;
2325 -}
2326 -
2071 static int extend_abbrev_len(const struct object_id *a,
2072 const struct object_id *b,
2073 unsigned *out)
packfile.h
+2 -15
@@ -227,21 +227,6 @@ int for_each_object_in_pack(struct packed_git *p,
227 each_packed_object_fn, void *data,
228 enum odb_for_each_object_flags flags);
229
230 -/*
231 - * Iterate through all packed objects in the given packfile store and invoke
232 - * the callback function for each of them. If an object info request is given,
233 - * then the object info will be read for every individual object and passed to
234 - * the callback as if `packfile_store_read_object_info()` was called for the
235 - * object.
236 - *
237 - * The flags parameter is a combination of `odb_for_each_object_flags`.
238 - */
239 -int packfile_store_for_each_object(struct odb_source_packed *store,
240 - const struct object_info *request,
241 - odb_for_each_object_cb cb,
242 - void *cb_data,
243 - const struct odb_for_each_object_options *opts);
244 -
230 int packfile_store_find_abbrev_len(struct odb_source_packed *store,
231 const struct object_id *oid,
232 unsigned min_len,
@@ -354,6 +339,8 @@ extern int do_check_packed_object_crc;
339 */
340 int packed_object_info(struct packed_git *pack,
341 off_t offset, struct object_info *);
342 +int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
343 + uint32_t *maybe_index_pos, struct object_info *oi);
344
345 void mark_bad_packed_object(struct packed_git *, const struct object_id *);
346 const struct packed_git *has_packed_and_bad(struct repository *, const struct object_id *);