Merge branch 'ps/object-source-loose'

A part of code paths that deals with loose objects has been cleaned
up.

* ps/object-source-loose:
  object-file: refactor writing objects via a stream
  object-file: rename `write_object_file()`
  object-file: refactor freshening of objects
  object-file: rename `has_loose_object()`
  object-file: read objects via the loose object source
  object-file: move loose object map into loose source
  object-file: hide internals when we need to reprepare loose sources
  object-file: move loose object cache into loose source
  object-file: introduce `struct odb_source_loose`
  object-file: move `fetch_if_missing`
  odb: adjust naming to free object sources
  odb: introduce `odb_source_new()`
  odb: fix subtle logic to check whether an alternate is usable
This commit is contained in:
Junio C Hamano
2025-11-24 15:46:41 -08:00
12 changed files with 287 additions and 207 deletions

View File

@@ -99,8 +99,8 @@ static int check_and_freshen_source(struct odb_source *source,
return check_and_freshen_file(path.buf, freshen);
}
int has_loose_object(struct odb_source *source,
const struct object_id *oid)
int odb_source_loose_has_object(struct odb_source *source,
const struct object_id *oid)
{
return check_and_freshen_source(source, oid, 0);
}
@@ -167,25 +167,22 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
}
/*
* Find "oid" as a loose object in the local repository or in an alternate.
* Find "oid" as a loose object in given source.
* Returns 0 on success, negative on failure.
*
* The "path" out-parameter will give the path of the object we found (if any).
* Note that it may point to static storage and is only valid until another
* call to stat_loose_object().
*/
static int stat_loose_object(struct repository *r, const struct object_id *oid,
static int stat_loose_object(struct odb_source_loose *loose,
const struct object_id *oid,
struct stat *st, const char **path)
{
struct odb_source *source;
static struct strbuf buf = STRBUF_INIT;
odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
*path = odb_loose_path(source, &buf, oid);
if (!lstat(*path, st))
return 0;
}
*path = odb_loose_path(loose->source, &buf, oid);
if (!lstat(*path, st))
return 0;
return -1;
}
@@ -194,39 +191,24 @@ static int stat_loose_object(struct repository *r, const struct object_id *oid,
* Like stat_loose_object(), but actually open the object and return the
* descriptor. See the caveats on the "path" parameter above.
*/
static int open_loose_object(struct repository *r,
static int open_loose_object(struct odb_source_loose *loose,
const struct object_id *oid, const char **path)
{
int fd;
struct odb_source *source;
int most_interesting_errno = ENOENT;
static struct strbuf buf = STRBUF_INIT;
int fd;
odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
*path = odb_loose_path(source, &buf, oid);
fd = git_open(*path);
if (fd >= 0)
return fd;
*path = odb_loose_path(loose->source, &buf, oid);
fd = git_open(*path);
if (fd >= 0)
return fd;
if (most_interesting_errno == ENOENT)
most_interesting_errno = errno;
}
errno = most_interesting_errno;
return -1;
}
static int quick_has_loose(struct repository *r,
static int quick_has_loose(struct odb_source_loose *loose,
const struct object_id *oid)
{
struct odb_source *source;
odb_prepare_alternates(r->objects);
for (source = r->objects->sources; source; source = source->next) {
if (oidtree_contains(odb_loose_cache(source, oid), oid))
return 1;
}
return 0;
return !!oidtree_contains(odb_source_loose_cache(loose->source, oid), oid);
}
/*
@@ -252,12 +234,12 @@ static void *map_fd(int fd, const char *path, unsigned long *size)
return map;
}
void *map_loose_object(struct repository *r,
const struct object_id *oid,
unsigned long *size)
void *odb_source_loose_map_object(struct odb_source *source,
const struct object_id *oid,
unsigned long *size)
{
const char *p;
int fd = open_loose_object(r, oid, &p);
int fd = open_loose_object(source->loose, oid, &p);
if (fd < 0)
return NULL;
@@ -407,9 +389,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)
return 0;
}
int loose_object_info(struct repository *r,
const struct object_id *oid,
struct object_info *oi, int flags)
int odb_source_loose_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi, int flags)
{
int status = 0;
int fd;
@@ -422,7 +404,7 @@ int loose_object_info(struct repository *r,
enum object_type type_scratch;
if (oi->delta_base_oid)
oidclr(oi->delta_base_oid, r->hash_algo);
oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
/*
* If we don't care about type or size, then we don't
@@ -435,15 +417,15 @@ int loose_object_info(struct repository *r,
if (!oi->typep && !oi->sizep && !oi->contentp) {
struct stat st;
if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
return quick_has_loose(r, oid) ? 0 : -1;
if (stat_loose_object(r, oid, &st, &path) < 0)
return quick_has_loose(source->loose, oid) ? 0 : -1;
if (stat_loose_object(source->loose, oid, &st, &path) < 0)
return -1;
if (oi->disk_sizep)
*oi->disk_sizep = st.st_size;
return 0;
}
fd = open_loose_object(r, oid, &path);
fd = open_loose_object(source->loose, oid, &path);
if (fd < 0) {
if (errno != ENOENT)
error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
@@ -986,35 +968,15 @@ static int write_loose_object(struct odb_source *source,
FOF_SKIP_COLLISION_CHECK);
}
static int freshen_loose_object(struct object_database *odb,
const struct object_id *oid)
int odb_source_loose_freshen_object(struct odb_source *source,
const struct object_id *oid)
{
odb_prepare_alternates(odb);
for (struct odb_source *source = odb->sources; source; source = source->next)
if (check_and_freshen_source(source, oid, 1))
return 1;
return 0;
return !!check_and_freshen_source(source, oid, 1);
}
static int freshen_packed_object(struct object_database *odb,
const struct object_id *oid)
{
struct pack_entry e;
if (!find_pack_entry(odb->repo, oid, &e))
return 0;
if (e.p->is_cruft)
return 0;
if (e.p->freshened)
return 1;
if (!freshen_file(e.p->pack_name))
return 0;
e.p->freshened = 1;
return 1;
}
int stream_loose_object(struct odb_source *source,
struct input_stream *in_stream, size_t len,
struct object_id *oid)
int odb_source_loose_write_stream(struct odb_source *source,
struct odb_write_stream *in_stream, size_t len,
struct object_id *oid)
{
const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
struct object_id compat_oid;
@@ -1091,12 +1053,10 @@ int stream_loose_object(struct odb_source *source,
die(_("deflateEnd on stream object failed (%d)"), ret);
close_loose_object(source, fd, tmp_file.buf);
if (freshen_packed_object(source->odb, oid) ||
freshen_loose_object(source->odb, oid)) {
if (odb_freshen_object(source->odb, oid)) {
unlink_or_warn(tmp_file.buf);
goto cleanup;
}
odb_loose_path(source, &filename, oid);
/* We finally know the object path, and create the missing dir. */
@@ -1124,10 +1084,10 @@ cleanup:
return err;
}
int write_object_file(struct odb_source *source,
const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
struct object_id *compat_oid_in, unsigned flags)
int odb_source_loose_write_object(struct odb_source *source,
const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
struct object_id *compat_oid_in, unsigned flags)
{
const struct git_hash_algo *algo = source->odb->repo->hash_algo;
const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
@@ -1155,8 +1115,7 @@ int write_object_file(struct odb_source *source,
* it out into .git/objects/??/?{38} file.
*/
write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
if (freshen_packed_object(source->odb, oid) ||
freshen_loose_object(source->odb, oid))
if (odb_freshen_object(source->odb, oid))
return 0;
if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
return -1;
@@ -1179,7 +1138,7 @@ int force_object_loose(struct odb_source *source,
int ret;
for (struct odb_source *s = source->odb->sources; s; s = s->next)
if (has_loose_object(s, oid))
if (odb_source_loose_has_object(s, oid))
return 0;
oi.typep = &type;
@@ -1806,44 +1765,49 @@ static int append_loose_object(const struct object_id *oid,
return 0;
}
struct oidtree *odb_loose_cache(struct odb_source *source,
const struct object_id *oid)
struct oidtree *odb_source_loose_cache(struct odb_source *source,
const struct object_id *oid)
{
int subdir_nr = oid->hash[0];
struct strbuf buf = STRBUF_INIT;
size_t word_bits = bitsizeof(source->loose_objects_subdir_seen[0]);
size_t word_bits = bitsizeof(source->loose->subdir_seen[0]);
size_t word_index = subdir_nr / word_bits;
size_t mask = (size_t)1u << (subdir_nr % word_bits);
uint32_t *bitmap;
if (subdir_nr < 0 ||
(size_t) subdir_nr >= bitsizeof(source->loose_objects_subdir_seen))
(size_t) subdir_nr >= bitsizeof(source->loose->subdir_seen))
BUG("subdir_nr out of range");
bitmap = &source->loose_objects_subdir_seen[word_index];
bitmap = &source->loose->subdir_seen[word_index];
if (*bitmap & mask)
return source->loose_objects_cache;
if (!source->loose_objects_cache) {
ALLOC_ARRAY(source->loose_objects_cache, 1);
oidtree_init(source->loose_objects_cache);
return source->loose->cache;
if (!source->loose->cache) {
ALLOC_ARRAY(source->loose->cache, 1);
oidtree_init(source->loose->cache);
}
strbuf_addstr(&buf, source->path);
for_each_file_in_obj_subdir(subdir_nr, &buf,
source->odb->repo->hash_algo,
append_loose_object,
NULL, NULL,
source->loose_objects_cache);
source->loose->cache);
*bitmap |= mask;
strbuf_release(&buf);
return source->loose_objects_cache;
return source->loose->cache;
}
void odb_clear_loose_cache(struct odb_source *source)
static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
{
oidtree_clear(source->loose_objects_cache);
FREE_AND_NULL(source->loose_objects_cache);
memset(&source->loose_objects_subdir_seen, 0,
sizeof(source->loose_objects_subdir_seen));
oidtree_clear(loose->cache);
FREE_AND_NULL(loose->cache);
memset(&loose->subdir_seen, 0,
sizeof(loose->subdir_seen));
}
void odb_source_loose_reprepare(struct odb_source *source)
{
odb_source_loose_clear_cache(source->loose);
}
static int check_stream_oid(git_zstream *stream,
@@ -1999,3 +1963,20 @@ void object_file_transaction_commit(struct odb_transaction *transaction)
transaction->odb->transaction = NULL;
free(transaction);
}
struct odb_source_loose *odb_source_loose_new(struct odb_source *source)
{
struct odb_source_loose *loose;
CALLOC_ARRAY(loose, 1);
loose->source = source;
return loose;
}
void odb_source_loose_free(struct odb_source_loose *loose)
{
if (!loose)
return;
odb_source_loose_clear_cache(loose);
loose_object_map_clear(&loose->map);
free(loose);
}