odb: introduce "in-memory" source

Next to our typical object database sources, each object database also
has an implicit source of "cached" objects. These cached objects only
exist in memory and some use cases:

  - They contain evergreen objects that we expect to always exist, like
    for example the empty tree.

  - They can be used to store temporary objects that we don't want to
    persist to disk, which is used by git-blame(1) to create a fake
    worktree commit.

Overall, their use is somewhat restricted though. For example, we don't
provide the ability to use it as a temporary object database source that
allows the user to write objects, but discard them after Git exists. So
while these cached objects behave almost like a source, they aren't used
as one.

This is about to change over the following commits, where we will turn
cached objects into a new "in-memory" source. This will allow us to use
it exactly the same as any other source by providing the same common
interface as the "files" source.

For now, the in-memory source only hosts the cached objects and doesn't
provide any logic yet. This will change with subsequent commits, where
we move respective functionality into the source.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt
2026-04-09 09:24:22 +02:00
committed by Junio C Hamano
parent 86adb3b430
commit 3789d4f2be
7 changed files with 67 additions and 10 deletions

View File

@@ -1218,6 +1218,7 @@ LIB_OBJS += object.o
LIB_OBJS += odb.o
LIB_OBJS += odb/source.o
LIB_OBJS += odb/source-files.o
LIB_OBJS += odb/source-inmemory.o
LIB_OBJS += odb/streaming.o
LIB_OBJS += odb/transaction.o
LIB_OBJS += oid-array.o

View File

@@ -404,6 +404,7 @@ libgit_sources = [
'odb.c',
'odb/source.c',
'odb/source-files.c',
'odb/source-inmemory.c',
'odb/streaming.c',
'odb/transaction.c',
'oid-array.c',

21
odb.c
View File

@@ -14,6 +14,7 @@
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
#include "odb/source-inmemory.h"
#include "packfile.h"
#include "path.h"
#include "promisor-remote.h"
@@ -53,9 +54,9 @@ static const struct cached_object *find_cached_object(struct object_database *ob
.type = OBJ_TREE,
.buf = "",
};
const struct cached_object_entry *co = object_store->cached_objects;
const struct cached_object_entry *co = object_store->inmemory_objects->objects;
for (size_t i = 0; i < object_store->cached_object_nr; i++, co++)
for (size_t i = 0; i < object_store->inmemory_objects->objects_nr; i++, co++)
if (oideq(&co->oid, oid))
return &co->value;
@@ -792,9 +793,10 @@ int odb_pretend_object(struct object_database *odb,
find_cached_object(odb, oid))
return 0;
ALLOC_GROW(odb->cached_objects,
odb->cached_object_nr + 1, odb->cached_object_alloc);
co = &odb->cached_objects[odb->cached_object_nr++];
ALLOC_GROW(odb->inmemory_objects->objects,
odb->inmemory_objects->objects_nr + 1,
odb->inmemory_objects->objects_alloc);
co = &odb->inmemory_objects->objects[odb->inmemory_objects->objects_nr++];
co->value.size = len;
co->value.type = type;
co_buf = xmalloc(len);
@@ -1083,6 +1085,7 @@ struct object_database *odb_new(struct repository *repo,
o->sources = odb_source_new(o, primary_source, true);
o->sources_tail = &o->sources->next;
o->alternate_db = xstrdup_or_null(secondary_sources);
o->inmemory_objects = odb_source_inmemory_new(o);
free(to_free);
@@ -1123,9 +1126,11 @@ void odb_free(struct object_database *o)
odb_close(o);
odb_free_sources(o);
for (size_t i = 0; i < o->cached_object_nr; i++)
free((char *) o->cached_objects[i].value.buf);
free(o->cached_objects);
for (size_t i = 0; i < o->inmemory_objects->objects_nr; i++)
free((char *) o->inmemory_objects->objects[i].value.buf);
free(o->inmemory_objects->objects);
free(o->inmemory_objects->base.path);
free(o->inmemory_objects);
string_list_clear(&o->submodule_source_paths, 0);

4
odb.h
View File

@@ -8,6 +8,7 @@
#include "thread-utils.h"
struct cached_object_entry;
struct odb_source_inmemory;
struct packed_git;
struct repository;
struct strbuf;
@@ -80,8 +81,7 @@ struct object_database {
* to write them into the object store (e.g. a browse-only
* application).
*/
struct cached_object_entry *cached_objects;
size_t cached_object_nr, cached_object_alloc;
struct odb_source_inmemory *inmemory_objects;
/*
* A fast, rough count of the number of objects in the repository.

12
odb/source-inmemory.c Normal file
View File

@@ -0,0 +1,12 @@
#include "git-compat-util.h"
#include "odb/source-inmemory.h"
struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
{
struct odb_source_inmemory *source;
CALLOC_ARRAY(source, 1);
odb_source_init(&source->base, odb, ODB_SOURCE_INMEMORY, "source", false);
return source;
}

35
odb/source-inmemory.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef ODB_SOURCE_INMEMORY_H
#define ODB_SOURCE_INMEMORY_H
#include "odb/source.h"
struct cached_object_entry;
/*
* An inmemory source that you can write objects to that shall be made
* available for reading, but that shouldn't ever be persisted to disk. Note
* that any objects written to this source will be stored in memory, so the
* number of objects you can store is limited by available system memory.
*/
struct odb_source_inmemory {
struct odb_source base;
struct cached_object_entry *objects;
size_t objects_nr, objects_alloc;
};
/* Create a new in-memory object database source. */
struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb);
/*
* Cast the given object database source to the inmemory backend. This will
* cause a BUG in case the source doesn't use this backend.
*/
static inline struct odb_source_inmemory *odb_source_inmemory_downcast(struct odb_source *source)
{
if (source->type != ODB_SOURCE_INMEMORY)
BUG("trying to downcast source of type '%d' to inmemory", source->type);
return container_of(source, struct odb_source_inmemory, base);
}
#endif

View File

@@ -13,6 +13,9 @@ enum odb_source_type {
/* The "files" backend that uses loose objects and packfiles. */
ODB_SOURCE_FILES,
/* The "inmemory" backend that stores objects in memory. */
ODB_SOURCE_INMEMORY,
};
struct object_id;