Merge branch 'ps/odb-generic-corrupt-objects' into en/midx-missing-pack-fallback

* 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
seen
Junio C Hamano 2026-08-25 13:49:15 -07:00
commit 48fc56621c
15 changed files with 224 additions and 121 deletions

View File

@ -1759,7 +1759,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;
}
}
@ -1786,7 +1786,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

@ -435,14 +435,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

@ -59,18 +59,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_read_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_read_stream **out,
@ -77,7 +105,7 @@ static int odb_source_packed_read_object_stream(struct odb_read_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

@ -27,6 +27,7 @@ enum odb_source_type {

struct object_id;
struct odb_read_stream;
struct strbuf;
struct strvec;

/*
@ -110,13 +111,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
@ -340,14 +345,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

@ -985,23 +985,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,
@ -1859,13 +1842,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)
@ -1941,7 +1928,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;
}

@ -1962,7 +1949,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

@ -294,7 +294,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,
@ -328,7 +329,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);
}