Merge branch 'ps/odb-drop-whence' into next

The 'whence' field in 'struct object_info' has been removed,
refactoring backend-specific object information retrieval into an opt-
in 'struct object_info_source' structure.

* ps/odb-drop-whence:
  odb: document object info fields
  odb: drop `whence` field from object info
  treewide: convert users of `whence` to the new source field
  odb: add `source` field to struct object_info_source
  odb: make backend-specific fields optional
  packfile: thread odb_source_packed through packed_object_info()
next
Junio C Hamano 2026-07-08 12:07:00 -07:00
commit f43ee51cc3
14 changed files with 130 additions and 69 deletions

View File

@ -500,7 +500,7 @@ static void batch_object_write(const char *obj_name,
data->info.sizep = &data->size;

if (pack)
ret = packed_object_info(pack, offset, &data->info);
ret = packed_object_info(NULL, pack, offset, &data->info);
else
ret = odb_read_object_info_extended(the_repository->objects,
&data->oid, &data->info,
@ -837,8 +837,9 @@ static int batch_one_object_oi(const struct object_id *oid,
void *_payload)
{
struct for_each_object_payload *payload = _payload;
if (oi && oi->whence == OI_PACKED)
return payload->callback(oid, oi->u.packed.pack, oi->u.packed.offset,
if (oi && oi->source_infop->source->type == ODB_SOURCE_PACKED)
return payload->callback(oid, oi->source_infop->u.packed.pack,
oi->source_infop->u.packed.offset,
payload->payload);
return payload->callback(oid, NULL, 0, payload->payload);
}
@ -909,7 +910,10 @@ static void batch_each_object(struct batch_options *opt,
&payload, flags);
}
} else {
struct object_info oi = { 0 };
struct odb_source_info source_info;
struct object_info oi = {
.source_infop = &source_info,
};

for (source = the_repository->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);

View File

@ -1825,11 +1825,16 @@ static void repack_local_links(void)

oidset_iter_init(&outgoing_links, &iter);
while ((oid = oidset_iter_next(&iter))) {
struct object_info info = OBJECT_INFO_INIT;
struct odb_source_info source_info;
struct object_info info = {
.source_infop = &source_info,
};

if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0))
/* Missing; assume it is a promisor object */
continue;
if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor)
if (source_info.source->type == ODB_SOURCE_PACKED &&
source_info.u.packed.pack->pack_promisor)
continue;

if (!cmd.args.nr) {

View File

@ -2463,7 +2463,7 @@ static void drop_reused_delta(struct object_entry *entry)

oi.sizep = &size;
oi.typep = &type;
if (packed_object_info(IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
if (packed_object_info(NULL, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
/*
* We failed to get the info from this pack for some reason;
* fall back to odb_read_object_info, which may find another copy.
@ -3820,7 +3820,7 @@ static int add_object_entry_from_pack(const struct object_id *oid,
ofs = nth_packed_object_offset(p, pos);

oi.typep = &type;
if (packed_object_info(p, ofs, &oi) < 0) {
if (packed_object_info(NULL, p, ofs, &oi) < 0) {
die(_("could not get type of object %s in pack %s"),
oid_to_hex(oid), p->pack_name);
} else if (type == OBJ_COMMIT) {
@ -4495,8 +4495,9 @@ static int add_object_in_unpacked_pack(const struct object_id *oid,
void *data UNUSED)
{
if (cruft) {
add_cruft_object_entry(oid, OBJ_NONE, oi->u.packed.pack,
oi->u.packed.offset, NULL, *oi->mtimep);
add_cruft_object_entry(oid, OBJ_NONE, oi->source_infop->u.packed.pack,
oi->source_infop->u.packed.offset, NULL,
*oi->mtimep);
} else {
add_object_entry(oid, OBJ_NONE, "", 0);
}
@ -4513,8 +4514,10 @@ static void add_objects_in_unpacked_packs(void)
ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS,
};
struct odb_source_info source_info;
struct object_info oi = {
.mtimep = &mtime,
.source_infop = &source_info,
};

odb_prepare_alternates(to_pack.repo->objects);
@ -5036,10 +5039,14 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED,

static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED)
{
struct object_info info = OBJECT_INFO_INIT;
struct odb_source_info source_info;
struct object_info info = {
.source_infop = &source_info,
};

if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0))
BUG("should_include_obj should only be called on existing objects");
return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor;
return source_info.source->type != ODB_SOURCE_PACKED || !source_info.u.packed.pack->pack_promisor;
}

static int is_not_in_promisor_pack(struct commit *commit, void *data) {

View File

@ -1538,7 +1538,7 @@ static int add_packed_commits(const struct object_id *oid,
struct object_info oi = OBJECT_INFO_INIT;

oi.typep = &type;
if (packed_object_info(pack, offset, &oi) < 0)
if (packed_object_info(NULL, pack, offset, &oi) < 0)
die(_("unable to get type of object %s"), oid_to_hex(oid));

return add_packed_commits_oi(oid, &oi, data);

4
odb.c
View File

@ -691,8 +691,8 @@ static int oid_object_info_convert(struct repository *r,
return -1;
}
}
input_oi->whence = new_oi.whence;
input_oi->u = new_oi.u;
if (input_oi->source_infop)
*input_oi->source_infop = *new_oi.source_infop;
return ret;
}


80
odb.h
View File

@ -260,33 +260,19 @@ int odb_pretend_object(struct object_database *odb,
void *buf, size_t len, enum object_type type,
struct object_id *oid);

struct object_info {
/* Request */
enum object_type *typep;
size_t *sizep;
off_t *disk_sizep;
struct object_id *delta_base_oid;
void **contentp;
/*
* Object database source information that can be used to uniquely identify an
* object and learn more about how exactly it is stored.
*/
struct odb_source_info {
/* The source that this object has been looked up from. */
struct odb_source *source;

/*
* The time the given looked-up object has been last modified.
*
* Note: the mtime may be ambiguous in case the object exists multiple
* times in the object database. It is thus _not_ recommended to use
* this field outside of contexts where you would read every instance
* of the object, like for example with `odb_for_each_object()`. As it
* is impossible to say at the ODB level what the intent of the caller
* is (e.g. whether to find the oldest or newest object), it is the
* responsibility of the caller to disambiguate the mtimes.
* Backend-specific information about the specific object. This can be
* used for example to uniquely identify a given object in case it
* exists multiple times.
*/
time_t *mtimep;

/* Response */
enum {
OI_CACHED,
OI_LOOSE,
OI_PACKED,
} whence;
union {
/*
* struct {
@ -309,6 +295,52 @@ struct object_info {
} u;
};

/*
* The object info contains the query and response that is to be used for
* functions that end up reading object information. Callers are expected to
* populate pointers whose information they want to request.
*/
struct object_info {
/* The object type. */
enum object_type *typep;

/* The inflated object size in bytes. */
size_t *sizep;

/* The object size as stored on disk. */
off_t *disk_sizep;

/*
* The base the object is deltified against, in case it is stored as a
* delta.
*/
struct object_id *delta_base_oid;

/* The object contents. Ownership of memory goes over to the caller. */
void **contentp;

/*
* The time the given looked-up object has been last modified.
*
* Note: the mtime may be ambiguous in case the object exists multiple
* times in the object database. It is thus _not_ recommended to use
* this field outside of contexts where you would read every instance
* of the object, like for example with `odb_for_each_object()`. As it
* is impossible to say at the ODB level what the intent of the caller
* is (e.g. whether to find the oldest or newest object), it is the
* responsibility of the caller to disambiguate the mtimes.
*/
time_t *mtimep;

/*
* Backend-specific information that tells the caller where exactly an
* object was looked up from. This information should help disambiguate
* object lookups in case the same object exists in multiple sources,
* or multiple times in the same source.
*/
struct odb_source_info *source_infop;
};

/*
* Initializer for a "struct object_info" that wants no items. You may
* also memset() the memory to all-zeroes.

View File

@ -52,7 +52,8 @@ static void populate_object_info(struct odb_source_inmemory *source,
*oi->contentp = xmemdupz(object->buf, object->size);
if (oi->mtimep)
*oi->mtimep = 0;
oi->whence = OI_CACHED;
if (oi->source_infop)
oi->source_infop->source = &source->base;
}

static int odb_source_inmemory_read_object_info(struct odb_source *source,

View File

@ -196,8 +196,8 @@ out:
oi->typep = NULL;
if (oi->delta_base_oid)
oidclr(oi->delta_base_oid, loose->base.odb->repo->hash_algo);
if (!ret)
oi->whence = OI_LOOSE;
if (oi->source_infop && !ret)
oi->source_infop->source = &loose->base;
}

return ret;

View File

@ -59,7 +59,7 @@ static int odb_source_packed_read_object_info(struct odb_source *source,
if (!oi)
return 0;

ret = packed_object_info(e.p, e.offset, oi);
ret = packed_object_info(packed, e.p, e.offset, oi);
if (ret < 0) {
mark_bad_packed_object(e.p, oid);
return -1;
@ -99,7 +99,7 @@ static int odb_source_packed_for_each_object_wrapper(const struct object_id *oid
off_t offset = nth_packed_object_offset(pack, index_pos);
struct object_info oi = *data->request;

if (packed_object_info_with_index_pos(pack, offset,
if (packed_object_info_with_index_pos(data->store, pack, offset,
&index_pos, &oi) < 0) {
mark_bad_packed_object(pack, oid);
return -1;

View File

@ -1877,7 +1877,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
ofs = pack_pos_to_offset(pack, pos);
}

if (packed_object_info(pack, ofs, &oi) < 0) {
if (packed_object_info(NULL, pack, ofs, &oi) < 0) {
struct object_id oid;
nth_bitmap_object_oid(bitmap_git, &oid,
pack_pos_to_index(pack, pos));

View File

@ -1324,7 +1324,8 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
hashmap_add(&delta_base_cache, &ent->ent);
}

int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
int packed_object_info_with_index_pos(struct odb_source_packed *source,
struct packed_git *p, off_t obj_offset,
uint32_t *maybe_index_pos, struct object_info *oi)
{
struct pack_window *w_curs = NULL;
@ -1420,23 +1421,28 @@ int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
oidclr(oi->delta_base_oid, p->repo->hash_algo);
}

oi->whence = OI_PACKED;
oi->u.packed.offset = obj_offset;
oi->u.packed.pack = p;
if (oi->source_infop) {
if (!source)
BUG("cannot request source without an owning source");
oi->source_infop->source = &source->base;

switch (type) {
case OBJ_NONE:
oi->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN;
break;
case OBJ_REF_DELTA:
oi->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA;
break;
case OBJ_OFS_DELTA:
oi->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA;
break;
default:
oi->u.packed.type = PACKED_OBJECT_TYPE_FULL;
break;
oi->source_infop->u.packed.offset = obj_offset;
oi->source_infop->u.packed.pack = p;

switch (type) {
case OBJ_NONE:
oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN;
break;
case OBJ_REF_DELTA:
oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA;
break;
case OBJ_OFS_DELTA:
oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA;
break;
default:
oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_FULL;
break;
}
}

ret = 0;
@ -1446,10 +1452,11 @@ out:
return ret;
}

int packed_object_info(struct packed_git *p, off_t obj_offset,
int packed_object_info(struct odb_source_packed *source,
struct packed_git *p, off_t obj_offset,
struct object_info *oi)
{
return packed_object_info_with_index_pos(p, obj_offset, NULL, oi);
return packed_object_info_with_index_pos(source, p, obj_offset, NULL, oi);
}

static void *unpack_compressed_entry(struct packed_git *p,

View File

@ -320,9 +320,11 @@ extern int do_check_packed_object_crc;
* Look up the object info for a specific offset in the packfile.
* Returns zero on success, a negative error code otherwise.
*/
int packed_object_info(struct packed_git *pack,
int packed_object_info(struct odb_source_packed *source,
struct packed_git *pack,
off_t offset, struct object_info *);
int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset,
int packed_object_info_with_index_pos(struct odb_source_packed *source,
struct packed_git *p, off_t obj_offset,
uint32_t *maybe_index_pos, struct object_info *oi);

void mark_bad_packed_object(struct packed_git *, const struct object_id *);

View File

@ -234,8 +234,9 @@ static int add_recent_object(const struct object_id *oid,

add_pending_object(data->revs, obj, "");
if (data->cb) {
if (oi->whence == OI_PACKED)
data->cb(obj, oi->u.packed.pack, oi->u.packed.offset, *oi->mtimep);
if (oi->source_infop->source->type == ODB_SOURCE_PACKED)
data->cb(obj, oi->source_infop->u.packed.pack,
oi->source_infop->u.packed.offset, *oi->mtimep);
else
data->cb(obj, NULL, 0, *oi->mtimep);
}
@ -252,9 +253,11 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
unsigned flags;
enum object_type type;
time_t mtime;
struct odb_source_info source_info;
struct object_info oi = {
.mtimep = &mtime,
.typep = &type,
.source_infop = &source_info,
};
int r;


View File

@ -52,7 +52,7 @@ static int add_packed_object(const struct object_id *oid,

entry = packlist_alloc(packed, oid);
entry->idx.offset = nth_packed_object_offset(pack, pos);
if (packed_object_info(pack, entry->idx.offset, &oi) < 0)
if (packed_object_info(NULL, pack, entry->idx.offset, &oi) < 0)
die("could not get type of object %s",
oid_to_hex(oid));
oe_set_type(entry, type);