Merge branch 'ps/odb-generic-corrupt-objects'

The object database (odb) API has been refactored to distinguish
between missing objects and corrupt ones by returning more
descriptive error statuses.  Both the packed and loose backends now
faithfully propagate error details using a generic strbuf error
mechanism, removing backend-specific leakage from central lookup
paths.

* ps/odb-generic-corrupt-objects:
  odb: handle `OBJECT_INFO_DIE_IF_CORRUPT` generically
  odb/source: allow `read_object_info()` to bubble up error messages
  odb/source: let callers discern missing and corrupt objects
  odb/source: introduce error status when reading objects
  odb/source-packed: flag known-bad objects as corrupt and not missing
main
Junio C Hamano 2026-08-31 08:24:59 -07:00
commit 6e6f4b582c
15 changed files with 224 additions and 121 deletions

View File

@ -1760,7 +1760,7 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
struct odb_source *source = the_repository->objects->sources->next;
for (; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0))
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0, NULL))
return 0;
}
}
@ -1787,7 +1787,7 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
struct multi_pack_index *m = get_multi_pack_index(files->packed);
struct pack_entry e;

if (m && fill_midx_entry(m, oid, &e)) {
if (m && fill_midx_entry(m, oid, &e, NULL)) {
want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset, found_mtime);
if (want != -1)
return want;
@ -4171,7 +4171,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type

for (; !found && source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0))
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0, NULL))
found = 1;
}

@ -4637,7 +4637,7 @@ static int force_object_loose(struct odb_source *source,

for (struct odb_source *s = source->odb->sources; s; s = s->next) {
struct odb_source_files *files = odb_source_files_downcast(s);
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0))
if (!odb_source_read_object_info(&files->loose->base, oid, NULL, 0, NULL))
return 0;
}


10
midx.c
View File

@ -591,7 +591,8 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos)

int fill_midx_entry(struct multi_pack_index *m,
const struct object_id *oid,
struct pack_entry *e)
struct pack_entry *e,
struct packed_git **bad_pack)
{
uint32_t pos;
uint32_t pack_int_id;
@ -618,8 +619,11 @@ int fill_midx_entry(struct multi_pack_index *m,
return 0;

if (oidset_size(&p->bad_objects) &&
oidset_contains(&p->bad_objects, oid))
oidset_contains(&p->bad_objects, oid)) {
if (bad_pack && !*bad_pack)
*bad_pack = p;
return 0;
}

e->offset = nth_midxed_offset(m, pos);
e->p = p;
@ -1028,7 +1032,7 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)

nth_midxed_object_oid(&oid, m, pairs[i].pos);

if (!fill_midx_entry(m, &oid, &e)) {
if (!fill_midx_entry(m, &oid, &e, NULL)) {
midx_report(_("failed to load pack entry for oid[%d] = %s"),
pairs[i].pos, oid_to_hex(&oid));
continue;

3
midx.h
View File

@ -117,7 +117,8 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos);
struct object_id *nth_midxed_object_oid(struct object_id *oid,
struct multi_pack_index *m,
uint32_t n);
int fill_midx_entry(struct multi_pack_index *m, const struct object_id *oid, struct pack_entry *e);
int fill_midx_entry(struct multi_pack_index *m, const struct object_id *oid,
struct pack_entry *e, struct packed_git **bad_pack);
int midx_contains_pack(struct multi_pack_index *m,
const char *idx_or_pack_name);
int midx_layer_contains_pack(struct multi_pack_index *m,

63
odb.c
View File

@ -15,7 +15,6 @@
#include "object-name.h"
#include "odb.h"
#include "odb/source-inmemory.h"
#include "packfile.h"
#include "path.h"
#include "promisor-remote.h"
#include "quote.h"
@ -547,12 +546,15 @@ static int register_all_submodule_sources(struct object_database *odb)
return ret;
}

static int do_oid_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi, unsigned flags)
static enum odb_read_status do_oid_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi, unsigned flags)
{
struct strbuf corrupt_err = STRBUF_INIT;
const struct object_id *real = oid;
enum odb_read_status ret;
int already_retried = 0;
bool corrupt = false;

if (flags & OBJECT_INFO_LOOKUP_REPLACE)
real = lookup_replace_object(odb->repo, oid);
@ -560,7 +562,7 @@ static int do_oid_object_info_extended(struct object_database *odb,
if (is_null_oid(real))
return -1;

if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags))
if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags, NULL))
return 0;

odb_prepare_alternates(odb);
@ -568,9 +570,14 @@ static int do_oid_object_info_extended(struct object_database *odb,
while (1) {
struct odb_source *source;

for (source = odb->sources; source; source = source->next)
if (!odb_source_read_object_info(source, real, oi, flags))
return 0;
for (source = odb->sources; source; source = source->next) {
ret = odb_source_read_object_info(source, real, oi, flags,
corrupt_err.len ? NULL : &corrupt_err);
if (!ret)
goto out;
if (ret != ODB_READ_NOT_FOUND)
corrupt = true;
}

/*
* When the object hasn't been found we try a second read and
@ -578,10 +585,15 @@ static int do_oid_object_info_extended(struct object_database *odb,
* caches or reload on-disk state.
*/
if (!(flags & OBJECT_INFO_QUICK)) {
for (source = odb->sources; source; source = source->next)
if (!odb_source_read_object_info(source, real, oi,
flags | OBJECT_INFO_SECOND_READ))
return 0;
for (source = odb->sources; source; source = source->next) {
ret = odb_source_read_object_info(source, real, oi,
flags | OBJECT_INFO_SECOND_READ,
corrupt_err.len ? NULL : &corrupt_err);
if (!ret)
goto out;
if (ret != ODB_READ_NOT_FOUND)
corrupt = true;
}
}

/*
@ -604,16 +616,23 @@ static int do_oid_object_info_extended(struct object_database *odb,
}

if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
const struct packed_git *p;
if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
die(_("replacement %s not found for %s"),
oid_to_hex(real), oid_to_hex(oid));
if ((p = has_packed_and_bad(odb->repo, real)))
die(_("packed object %s (stored in %s) is corrupt"),
oid_to_hex(real), p->pack_name);
if (corrupt) {
if (corrupt_err.len)
die("%s", corrupt_err.buf);
die(_("object %s is corrupt"), oid_to_hex(real));
}
}
return -1;

ret = corrupt ? ODB_READ_ERROR : ODB_READ_NOT_FOUND;
goto out;
}

out:
strbuf_release(&corrupt_err);
return ret;
}

static int oid_object_info_convert(struct repository *r,
@ -696,12 +715,12 @@ static int oid_object_info_convert(struct repository *r,
return ret;
}

int odb_read_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
enum odb_read_status odb_read_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
{
int ret;
enum odb_read_status ret;

if (oid->algo && (hash_algo_by_ptr(odb->repo->hash_algo) != oid->algo))
return oid_object_info_convert(odb->repo, oid, oi, flags);

17
odb.h
View File

@ -448,14 +448,23 @@ enum object_info_flags {
OBJECT_INFO_FOR_PREFETCH = (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK),
};

enum odb_read_status {
/* The read was successful. */
ODB_READ_OK = 0,
/* The read resulted in a generic error. */
ODB_READ_ERROR = -1,
/* The object could not be found. */
ODB_READ_NOT_FOUND = -2,
};

/*
* Read object info from the object database and populate the `object_info`
* structure. Returns 0 on success, a negative error code otherwise.
*/
int odb_read_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags);
enum odb_read_status odb_read_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags);

/*
* Read a subset of object info for the given object ID. Returns an `enum

View File

@ -75,18 +75,35 @@ static void odb_source_files_prepare(struct odb_source *source,
odb_source_prepare(&files->packed->base, flags);
}

static int odb_source_files_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
static enum odb_read_status odb_source_files_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags,
struct strbuf *errmsg)
{
struct odb_source_files *files = odb_source_files_downcast(source);
enum odb_read_status ret_packed, ret_loose;

if (!odb_source_read_object_info(&files->packed->base, oid, oi, flags) ||
!odb_source_read_object_info(&files->loose->base, oid, oi, flags))
ret_packed = odb_source_read_object_info(&files->packed->base, oid, oi,
flags, errmsg);
if (!ret_packed)
return 0;

return -1;
ret_loose = odb_source_read_object_info(&files->loose->base, oid, oi, flags,
ret_packed == ODB_READ_NOT_FOUND ? errmsg : NULL);
if (!ret_loose)
return 0;

/*
* Reading the packed object may have failed even though the object
* exists, for example because it is corrupt. Report this failure to
* the caller in case neither of the sources was able to read the
* object, and prefer the error of the packed source in case both
* reads have failed.
*/
if (ret_packed != ODB_READ_NOT_FOUND)
return ret_packed;
return ret_loose;
}

static int odb_source_files_read_object_stream(struct odb_stream **out,

View File

@ -56,17 +56,18 @@ static void populate_object_info(struct odb_source_inmemory *source,
oi->source_infop->source = &source->base;
}

static int odb_source_inmemory_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags UNUSED)
static enum odb_read_status odb_source_inmemory_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags UNUSED,
struct strbuf *errmsg UNUSED)
{
struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
const struct inmemory_object *object;

object = find_cached_object(inmemory, oid);
if (!object)
return -1;
return ODB_READ_NOT_FOUND;

populate_object_info(inmemory, oi, object);
return 0;

View File

@ -67,7 +67,8 @@ static int read_object_info_from_path(struct odb_source_loose *loose,
const char *path,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
enum object_info_flags flags,
struct strbuf *errmsg)
{
int ret;
int fd;
@ -91,11 +92,16 @@ static int read_object_info_from_path(struct odb_source_loose *loose,
struct stat st;

if ((!oi || (!oi->disk_sizep && !oi->mtimep)) && (flags & OBJECT_INFO_QUICK)) {
ret = quick_has_loose(loose, oid) ? 0 : -1;
ret = quick_has_loose(loose, oid) ? 0 : ODB_READ_NOT_FOUND;
goto out;
}

if (lstat(path, &st) < 0) {
if (errno == ENOENT) {
ret = ODB_READ_NOT_FOUND;
goto out;
}

ret = -1;
goto out;
}
@ -113,9 +119,12 @@ static int read_object_info_from_path(struct odb_source_loose *loose,

fd = git_open(path);
if (fd < 0) {
if (errno != ENOENT)
error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
ret = -1;
if (errno == ENOENT) {
ret = ODB_READ_NOT_FOUND;
goto out;
}

ret = error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
goto out;
}

@ -155,7 +164,7 @@ static int read_object_info_from_path(struct odb_source_loose *loose,

if (parse_loose_header(hdr, oi) < 0) {
ret = error(_("unable to parse %s header"), oid_to_hex(oid));
goto corrupt;
goto out;
}

if (*oi->typep < 0)
@ -165,7 +174,7 @@ static int read_object_info_from_path(struct odb_source_loose *loose,
*oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
if (!*oi->contentp) {
ret = -1;
goto corrupt;
goto out;
}
}

@ -173,21 +182,19 @@ static int read_object_info_from_path(struct odb_source_loose *loose,
case ULHR_BAD:
ret = error(_("unable to unpack %s header"),
oid_to_hex(oid));
goto corrupt;
goto out;
case ULHR_TOO_LONG:
ret = error(_("header for %s too long, exceeds %d bytes"),
oid_to_hex(oid), MAX_HEADER_LEN);
goto corrupt;
goto out;
}

ret = 0;

corrupt:
if (ret && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
die(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(oid), path);

out:
if (ret && ret != ODB_READ_NOT_FOUND && errmsg)
strbuf_addf(errmsg, _("loose object %s (stored in %s) is corrupt"),
oid_to_hex(oid), path);
if (stream_to_end)
git_inflate_end(stream_to_end);
if (map)
@ -206,10 +213,11 @@ out:
return ret;
}

static int odb_source_loose_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
static enum odb_read_status odb_source_loose_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags,
struct strbuf *errmsg)
{
struct odb_source_loose *loose = odb_source_loose_downcast(source);
static struct strbuf buf = STRBUF_INIT;
@ -221,10 +229,10 @@ static int odb_source_loose_read_object_info(struct odb_source *source,
* second time.
*/
if (flags & OBJECT_INFO_SECOND_READ)
return -1;
return ODB_READ_NOT_FOUND;

odb_loose_path(loose, &buf, oid);
return read_object_info_from_path(loose, buf.buf, oid, oi, flags);
return read_object_info_from_path(loose, buf.buf, oid, oi, flags, errmsg);
}

/*
@ -421,7 +429,7 @@ static int for_each_object_wrapper_cb(const struct object_id *oid,
if (data->request) {
struct object_info oi = *data->request;

if (read_object_info_from_path(data->loose, path, oid, &oi, 0) < 0)
if (read_object_info_from_path(data->loose, path, oid, &oi, 0, NULL) < 0)
return -1;

return data->cb(oid, &oi, data->cb_data);
@ -439,7 +447,7 @@ static int for_each_prefixed_object_wrapper_cb(const struct object_id *oid,
struct object_info oi = *data->request;

if (odb_source_read_object_info(&data->loose->base,
oid, &oi, 0) < 0)
oid, &oi, 0, NULL) < 0)
return -1;

return data->cb(oid, &oi, data->cb_data);

View File

@ -2,7 +2,9 @@
#include "abspath.h"
#include "chdir-notify.h"
#include "dir.h"
#include "gettext.h"
#include "git-zlib.h"
#include "hex.h"
#include "list-objects-filter-options.h"
#include "mergesort.h"
#include "midx.h"
@ -10,21 +12,23 @@
#include "odb/streaming.h"
#include "packfile.h"
#include "pack-bitmap.h"
#include "strbuf.h"

static int find_pack_entry(struct odb_source_packed *store,
const struct object_id *oid,
struct pack_entry *e)
struct pack_entry *e,
struct packed_git **bad_pack)
{
struct packfile_list_entry *l;

odb_source_prepare(&store->base, 0);
if (store->midx && fill_midx_entry(store->midx, oid, e))
if (store->midx && fill_midx_entry(store->midx, oid, e, bad_pack))
return 1;

for (l = store->packs.head; l; l = l->next) {
struct packed_git *p = l->pack;

if (!p->multi_pack_index && packfile_fill_entry(p, oid, e)) {
if (!p->multi_pack_index && packfile_fill_entry(p, oid, e, bad_pack)) {
if (!store->skip_mru_updates)
packfile_list_prepend(&store->packs, p);
return 1;
@ -34,12 +38,14 @@ static int find_pack_entry(struct odb_source_packed *store,
return 0;
}

static int odb_source_packed_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
static enum odb_read_status odb_source_packed_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags,
struct strbuf *errmsg)
{
struct odb_source_packed *packed = odb_source_packed_downcast(source);
struct packed_git *bad_pack = NULL;
struct pack_entry e;
int ret;

@ -51,23 +57,45 @@ static int odb_source_packed_read_object_info(struct odb_source *source,
if (flags & OBJECT_INFO_SECOND_READ)
odb_source_prepare(source, ODB_PREPARE_FLUSH_CACHES);

if (!find_pack_entry(packed, oid, &e))
return 1;
if (!find_pack_entry(packed, oid, &e, &bad_pack)) {
/*
* The lookup may have failed because the object is known to be
* corrupt in one of the packfiles. Report the object as
* corrupt instead of missing in that case.
*/
if (bad_pack) {
ret = -1;
goto out;
}

ret = ODB_READ_NOT_FOUND;
goto out;
}

/*
* We know that the caller doesn't actually need the
* information below, so return early.
*/
if (!oi)
return 0;
if (!oi) {
ret = 0;
goto out;
}

ret = packed_object_info(packed, e.p, e.offset, oi);
if (ret < 0) {
bad_pack = e.p;
mark_bad_packed_object(e.p, oid);
return -1;
goto out;
}

return 0;
ret = 0;

out:
if (ret < 0 && bad_pack && errmsg)
strbuf_addf(errmsg, _("packed object %s (stored in %s) is corrupt"),
oid_to_hex(oid), bad_pack->pack_name);

return ret;
}

static int odb_source_packed_read_object_stream(struct odb_stream **out,
@ -77,7 +105,7 @@ static int odb_source_packed_read_object_stream(struct odb_stream **out,
struct odb_source_packed *packed = odb_source_packed_downcast(source);
struct pack_entry e;

if (!find_pack_entry(packed, oid, &e))
if (!find_pack_entry(packed, oid, &e, NULL))
return -1;

return packfile_read_object_stream(out, oid, e.p, e.offset);
@ -583,7 +611,7 @@ static int odb_source_packed_freshen_object(struct odb_source *source,
timesp = &times;
}

if (!find_pack_entry(packed, oid, &e))
if (!find_pack_entry(packed, oid, &e, NULL))
return 0;
if (e.p->is_cruft)
return 0;

View File

@ -33,6 +33,7 @@ const char *odb_source_type_to_name(enum odb_source_type type);

struct object_id;
struct odb_stream;
struct strbuf;
struct strvec;

/*
@ -128,13 +129,17 @@ struct odb_source {
* second read in case they know that the first read would have
* already surfaced the object without reloading any on-disk state.
*
* The callback is expected to return a negative error code in case
* reading the object has failed, 0 otherwise.
* The callback is expected to return an `enum odb_read_status`. Please
* refer to the individual values that can be returned. In case reading
* the object has failed with a generic error and `errmsg` is non-NULL,
* the callback is expected to populate it with a human-readable
* message that describes the failure.
*/
int (*read_object_info)(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags);
enum odb_read_status (*read_object_info)(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags,
struct strbuf *errmsg);

/*
* This callback is expected to create a new read stream that can be
@ -369,14 +374,19 @@ static inline void odb_source_prepare(struct odb_source *source,

/*
* Read an object from the object database source identified by its object ID.
* Returns 0 on success, a negative error code otherwise.
* Please refer to `enum odb_read_status` for the individual error codes.
*
* In case reading the object has failed with a generic error and `errmsg` is
* non-NULL it will be populated with a human-readable message that describes
* the failure.
*/
static inline int odb_source_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags)
static inline enum odb_read_status odb_source_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi,
enum object_info_flags flags,
struct strbuf *errmsg)
{
return source->read_object_info(source, oid, oi, flags);
return source->read_object_info(source, oid, oi, flags, errmsg);
}

/*

View File

@ -983,23 +983,6 @@ void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid)
oidset_insert(&p->bad_objects, oid);
}

const struct packed_git *has_packed_and_bad(struct repository *r,
const struct object_id *oid)
{
struct odb_source *source;

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

for (e = files->packed->packs.head; e; e = e->next)
if (oidset_contains(&e->pack->bad_objects, oid))
return e->pack;
}

return NULL;
}

off_t get_delta_base(struct packed_git *p,
struct pack_window **w_curs,
off_t *curpos,
@ -1857,13 +1840,17 @@ int is_pack_valid(struct packed_git *p)

int packfile_fill_entry(struct packed_git *p,
const struct object_id *oid,
struct pack_entry *e)
struct pack_entry *e,
struct packed_git **bad_pack)
{
off_t offset;

if (oidset_size(&p->bad_objects) &&
oidset_contains(&p->bad_objects, oid))
oidset_contains(&p->bad_objects, oid)) {
if (bad_pack && !*bad_pack)
*bad_pack = p;
return 0;
}

offset = find_pack_entry_one(oid, p);
if (!offset)
@ -1939,7 +1926,7 @@ int has_object_pack(struct repository *r, const struct object_id *oid)
odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
if (!odb_source_read_object_info(&files->packed->base, oid, NULL, 0))
if (!odb_source_read_object_info(&files->packed->base, oid, NULL, 0, NULL))
return 1;
}

@ -1960,7 +1947,7 @@ int has_object_kept_pack(struct repository *r, const struct object_id *oid,

for (; *cache; cache++) {
struct packed_git *p = *cache;
if (packfile_fill_entry(p, oid, &e))
if (packfile_fill_entry(p, oid, &e, NULL))
return 1;
}
}

View File

@ -295,7 +295,8 @@ off_t find_pack_entry_one(const struct object_id *oid, struct packed_git *);

int packfile_fill_entry(struct packed_git *p,
const struct object_id *oid,
struct pack_entry *e);
struct pack_entry *e,
struct packed_git **bad_pack);

int is_pack_valid(struct packed_git *);
void *unpack_entry(struct repository *r, struct packed_git *, off_t,
@ -330,7 +331,6 @@ int packed_object_info_with_index_pos(struct odb_source_packed *source,
uint32_t *maybe_index_pos, struct object_info *oi);

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

int has_object_pack(struct repository *r, const struct object_id *oid);
int has_object_kept_pack(struct repository *r, const struct object_id *oid,

View File

@ -82,7 +82,7 @@ static int read_midx_file(const char *object_dir, const char *checksum,
for (i = 0; i < m->num_objects; i++) {
nth_midxed_object_oid(&oid, m,
i + m->num_objects_in_base);
fill_midx_entry(m, &oid, &e);
fill_midx_entry(m, &oid, &e, NULL);

printf("%s %"PRIu64"\t%s\n",
oid_to_hex(&oid), e.offset, e.p->pack_name);

View File

@ -145,4 +145,22 @@ test_expect_success 'partial clone of corrupted repository' '
test_must_fail git -C corrupt-partial checkout --force
'

test_expect_success 'corrupted loose commit can be read from alternate' '
git init repo-a &&
tree=$(git -C repo-a write-tree) &&
commit=$(git -C repo-a commit-tree $tree </dev/null) &&

cp -r repo-a repo-b &&
(
cd repo-b &&
echo ../../../repo-a/.git/objects >.git/objects/info/alternates &&
corrupt_byte "$commit" 1
) &&

git -C repo-a cat-file -p "$commit" >expect &&
git -C repo-b cat-file -p "$commit" >actual 2>err &&
test_cmp expect actual &&
test_grep "inflate: data stream error" err
'

test_done

View File

@ -29,7 +29,7 @@ static void cl_assert_object_info(struct odb_source_inmemory *source,
.contentp = &actual_content,
};

cl_must_pass(odb_source_read_object_info(&source->base, oid, &oi, 0));
cl_must_pass(odb_source_read_object_info(&source->base, oid, &oi, 0, NULL));
cl_assert_equal_u(actual_size, strlen(expected_content));
cl_assert_equal_u(actual_type, expected_type);
cl_assert_equal_s((char *) actual_content, expected_content);
@ -72,7 +72,8 @@ void test_odb_inmemory__read_missing_object(void)
const char *end;

cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
cl_must_fail(odb_source_read_object_info(&source->base, &oid, NULL, 0));
cl_assert_equal_i(odb_source_read_object_info(&source->base, &oid, NULL, 0, NULL),
ODB_READ_NOT_FOUND);

odb_source_free(&source->base);
}