reachable: convert to use `odb_for_each_object()`

To figure out which objects expired objects we enumerate all loose and packed objects individually so that we can figure out their respective mtimes. Refactor the code to instead use `odb_for_each_object()` with a request that ask for the object mtime instead. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 26, 2026 at 10:51 UTC 7a8582c82ce896d89bbcc1d91d8b5bdc31902416
1 file changed +35 -90
reachable.c
+35 -90
@@ -191,30 +191,27 @@ static int obj_is_recent(const struct object_id *oid, timestamp_t mtime,
191 return oidset_contains(&data->extra_recent_oids, oid);
192 }
193
194 -static void add_recent_object(const struct object_id *oid,
195 - struct packed_git *pack,
196 - off_t offset,
197 - timestamp_t mtime,
198 - struct recent_data *data)
194 +static int want_recent_object(struct recent_data *data,
195 + const struct object_id *oid)
196 {
200 - struct object *obj;
201 - enum object_type type;
197 + if (data->ignore_in_core_kept_packs &&
198 + has_object_kept_pack(data->revs->repo, oid, KEPT_PACK_IN_CORE))
199 + return 0;
200 + return 1;
201 +}
202
203 - if (!obj_is_recent(oid, mtime, data))
204 - return;
203 +static int add_recent_object(const struct object_id *oid,
204 + struct object_info *oi,
205 + void *cb_data)
206 +{
207 + struct recent_data *data = cb_data;
208 + struct object *obj;
209
206 - /*
207 - * We do not want to call parse_object here, because
208 - * inflating blobs and trees could be very expensive.
209 - * However, we do need to know the correct type for
210 - * later processing, and the revision machinery expects
211 - * commits and tags to have been parsed.
212 - */
213 - type = odb_read_object_info(the_repository->objects, oid, NULL);
214 - if (type < 0)
215 - die("unable to get object info for %s", oid_to_hex(oid));
210 + if (!want_recent_object(data, oid) ||
211 + !obj_is_recent(oid, *oi->mtimep, data))
212 + return 0;
213
217 - switch (type) {
214 + switch (*oi->typep) {
215 case OBJ_TAG:
216 case OBJ_COMMIT:
217 obj = parse_object_or_die(the_repository, oid, NULL);
@@ -227,77 +224,22 @@ static void add_recent_object(const struct object_id *oid,
224 break;
225 default:
226 die("unknown object type for %s: %s",
230 - oid_to_hex(oid), type_name(type));
227 + oid_to_hex(oid), type_name(*oi->typep));
228 }
229
230 if (!obj)
231 die("unable to lookup %s", oid_to_hex(oid));
235 -
236 - add_pending_object(data->revs, obj, "");
237 - if (data->cb)
238 - data->cb(obj, pack, offset, mtime);
239 -}
240 -
241 -static int want_recent_object(struct recent_data *data,
242 - const struct object_id *oid)
243 -{
244 - if (data->ignore_in_core_kept_packs &&
245 - has_object_kept_pack(data->revs->repo, oid, KEPT_PACK_IN_CORE))
232 + if (obj->flags & SEEN)
233 return 0;
247 - return 1;
248 -}
234
250 -static int add_recent_loose(const struct object_id *oid,
251 - const char *path, void *data)
252 -{
253 - struct stat st;
254 - struct object *obj;
255 -
256 - if (!want_recent_object(data, oid))
257 - return 0;
258 -
259 - obj = lookup_object(the_repository, oid);
260 -
261 - if (obj && obj->flags & SEEN)
262 - return 0;
263 -
264 - if (stat(path, &st) < 0) {
265 - /*
266 - * It's OK if an object went away during our iteration; this
267 - * could be due to a simultaneous repack. But anything else
268 - * we should abort, since we might then fail to mark objects
269 - * which should not be pruned.
270 - */
271 - if (errno == ENOENT)
272 - return 0;
273 - return error_errno("unable to stat %s", oid_to_hex(oid));
235 + add_pending_object(data->revs, obj, "");
236 + if (data->cb) {
237 + if (oi->whence == OI_PACKED)
238 + data->cb(obj, oi->u.packed.pack, oi->u.packed.offset, *oi->mtimep);
239 + else
240 + data->cb(obj, NULL, 0, *oi->mtimep);
241 }
242
276 - add_recent_object(oid, NULL, 0, st.st_mtime, data);
277 - return 0;
278 -}
279 -
280 -static int add_recent_packed(const struct object_id *oid,
281 - struct packed_git *p,
282 - uint32_t pos,
283 - void *data)
284 -{
285 - struct object *obj;
286 - timestamp_t mtime = p->mtime;
287 -
288 - if (!want_recent_object(data, oid))
289 - return 0;
290 -
291 - obj = lookup_object(the_repository, oid);
292 -
293 - if (obj && obj->flags & SEEN)
294 - return 0;
295 - if (p->is_cruft) {
296 - if (load_pack_mtimes(p) < 0)
297 - die(_("could not load cruft pack .mtimes"));
298 - mtime = nth_packed_mtime(p, pos);
299 - }
300 - add_recent_object(oid, p, nth_packed_object_offset(p, pos), mtime, data);
243 return 0;
244 }
245
@@ -307,7 +249,13 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
249 int ignore_in_core_kept_packs)
250 {
251 struct recent_data data;
310 - enum odb_for_each_object_flags flags;
252 + unsigned flags;
253 + enum object_type type;
254 + time_t mtime;
255 + struct object_info oi = {
256 + .mtimep = &mtime,
257 + .typep = &type,
258 + };
259 int r;
260
261 data.revs = revs;
@@ -318,16 +266,13 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
266 oidset_init(&data.extra_recent_oids, 0);
267 data.extra_recent_oids_loaded = 0;
268
321 - r = for_each_loose_object(the_repository->objects, add_recent_loose, &data,
322 - ODB_FOR_EACH_OBJECT_LOCAL_ONLY);
323 - if (r)
324 - goto done;
325 -
269 flags = ODB_FOR_EACH_OBJECT_LOCAL_ONLY | ODB_FOR_EACH_OBJECT_PACK_ORDER;
270 if (ignore_in_core_kept_packs)
271 flags |= ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS;
272
330 - r = for_each_packed_object(revs->repo, add_recent_packed, &data, flags);
273 + r = odb_for_each_object(revs->repo->objects, &oi, add_recent_object, &data, flags);
274 + if (r)
275 + goto done;
276
277 done:
278 oidset_clear(&data.extra_recent_oids);