mirror of
https://github.com/git/git.git
synced 2026-08-08 17:11:48 +00:00
Merge branch 'ps/odb-make-creation-pluggable' into seen
The creation of the on-disk data structures for the object database has been made pluggable, allowing future backends to customize their setup. As part of this, the initialization of the object database has been deferred, and the loading of the loose-object map has been detangled from repository initialization. * ps/odb-make-creation-pluggable: odb: make creation of on-disk structures pluggable odb/source: introduce function to map source type to name setup: defer object database creation setup: handle ODB-related environment variables in `odb_new()` setup: detangle loading of loose object maps loose: load loose object map for the correct source
This commit is contained in:
25
loose.c
25
loose.c
@@ -61,12 +61,17 @@ static int insert_loose_map(struct odb_source_loose *loose,
|
||||
return inserted;
|
||||
}
|
||||
|
||||
static int load_one_loose_object_map(struct repository *repo, struct odb_source_loose *loose)
|
||||
int loose_object_map_load(struct odb_source_loose *loose)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
|
||||
struct repository *repo = loose->base.odb->repo;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
char *path;
|
||||
FILE *fp;
|
||||
int ret = -1;
|
||||
|
||||
if (!should_use_loose_object_map(repo))
|
||||
return 0;
|
||||
|
||||
if (!loose->map)
|
||||
loose_object_map_init(&loose->map);
|
||||
if (!loose->cache) {
|
||||
@@ -78,10 +83,10 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_
|
||||
insert_loose_map(loose, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
|
||||
insert_loose_map(loose, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
|
||||
|
||||
repo_common_path_replace(repo, &path, "objects/loose-object-idx");
|
||||
fp = fopen(path.buf, "rb");
|
||||
path = xstrfmt("%s/loose-object-idx", loose->base.path);
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
strbuf_release(&path);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,7 +107,7 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_
|
||||
err:
|
||||
fclose(fp);
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&path);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -110,17 +115,13 @@ int repo_read_loose_object_map(struct repository *repo)
|
||||
{
|
||||
struct odb_source *source;
|
||||
|
||||
if (!should_use_loose_object_map(repo))
|
||||
return 0;
|
||||
|
||||
odb_prepare_alternates(repo->objects);
|
||||
|
||||
for (source = repo->objects->sources; source; source = source->next) {
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
if (load_one_loose_object_map(repo, files->loose) < 0) {
|
||||
if (loose_object_map_load(files->loose) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
1
loose.h
1
loose.h
@@ -13,6 +13,7 @@ struct loose_object_map {
|
||||
|
||||
void loose_object_map_init(struct loose_object_map **map);
|
||||
void loose_object_map_clear(struct loose_object_map **map);
|
||||
int loose_object_map_load(struct odb_source_loose *loose);
|
||||
int repo_loose_object_map_oid(struct repository *repo,
|
||||
const struct object_id *src,
|
||||
const struct git_hash_algo *dest_algo,
|
||||
|
||||
21
odb.c
21
odb.c
@@ -1047,26 +1047,29 @@ bool odb_optimize_required(struct object_database *odb,
|
||||
}
|
||||
|
||||
struct object_database *odb_new(struct repository *repo,
|
||||
const char *primary_source,
|
||||
const char *secondary_sources)
|
||||
enum odb_new_flags flags)
|
||||
{
|
||||
struct object_database *o = xmalloc(sizeof(*o));
|
||||
char *to_free = NULL;
|
||||
char *primary_source = NULL, *secondary_sources = NULL;
|
||||
struct object_database *o;
|
||||
|
||||
memset(o, 0, sizeof(*o));
|
||||
CALLOC_ARRAY(o, 1);
|
||||
o->repo = repo;
|
||||
pthread_mutex_init(&o->replace_mutex, NULL);
|
||||
string_list_init_dup(&o->submodule_source_paths);
|
||||
|
||||
if (flags & ODB_NEW_HONOR_ENV) {
|
||||
primary_source = xstrdup_or_null(getenv(DB_ENVIRONMENT));
|
||||
secondary_sources = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
|
||||
}
|
||||
if (!primary_source)
|
||||
primary_source = to_free = xstrfmt("%s/objects", repo->commondir);
|
||||
primary_source = xstrfmt("%s/objects", repo->commondir);
|
||||
|
||||
o->sources = odb_source_new(o, primary_source, true);
|
||||
o->sources_tail = &o->sources->next;
|
||||
o->alternate_db = xstrdup_or_null(secondary_sources);
|
||||
o->alternate_db = secondary_sources;
|
||||
o->inmemory_objects = &odb_source_inmemory_new(o)->base;
|
||||
|
||||
free(to_free);
|
||||
|
||||
free(primary_source);
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
17
odb.h
17
odb.h
@@ -100,6 +100,20 @@ struct object_database {
|
||||
struct string_list submodule_source_paths;
|
||||
};
|
||||
|
||||
enum odb_new_flags {
|
||||
/*
|
||||
* Honor environment variables when constructing the object database
|
||||
* sources. This makes us respect the following environment variables:
|
||||
*
|
||||
* - GIT_OBJECT_DIRECTORY to override the primary object directory.
|
||||
*
|
||||
* - GIT_ALTERNATE_OBJECT_DIRECTORIES to override alternates.
|
||||
*
|
||||
* Environment variables may be backend-specific.
|
||||
*/
|
||||
ODB_NEW_HONOR_ENV = (1 << 0),
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a new object database for the given repository.
|
||||
*
|
||||
@@ -112,8 +126,7 @@ struct object_database {
|
||||
* Returns the newly created object database.
|
||||
*/
|
||||
struct object_database *odb_new(struct repository *repo,
|
||||
const char *primary_source,
|
||||
const char *alternate_sources);
|
||||
enum odb_new_flags flags);
|
||||
|
||||
/* Free the object database and release all resources. */
|
||||
void odb_free(struct object_database *o);
|
||||
|
||||
@@ -51,6 +51,23 @@ static void odb_source_files_close(struct odb_source *source)
|
||||
odb_source_close(&files->packed->base);
|
||||
}
|
||||
|
||||
static int odb_source_files_create_on_disk(struct odb_source *source)
|
||||
{
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
|
||||
safe_create_dir(source->odb->repo, source->path, 1);
|
||||
|
||||
strbuf_addf(&path, "%s/pack", source->path);
|
||||
safe_create_dir(source->odb->repo, path.buf, 1);
|
||||
|
||||
strbuf_reset(&path);
|
||||
strbuf_addf(&path, "%s/info", source->path);
|
||||
safe_create_dir(source->odb->repo, path.buf, 1);
|
||||
|
||||
strbuf_release(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void odb_source_files_prepare(struct odb_source *source,
|
||||
enum odb_prepare_flags flags)
|
||||
{
|
||||
@@ -742,6 +759,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
|
||||
|
||||
files->base.free = odb_source_files_free;
|
||||
files->base.close = odb_source_files_close;
|
||||
files->base.create_on_disk = odb_source_files_create_on_disk;
|
||||
files->base.prepare = odb_source_files_prepare;
|
||||
files->base.read_object_info = odb_source_files_read_object_info;
|
||||
files->base.read_object_stream = odb_source_files_read_object_stream;
|
||||
|
||||
@@ -43,7 +43,9 @@ bool odb_source_files_optimize_required(struct odb_source *source,
|
||||
static inline struct odb_source_files *odb_source_files_downcast(struct odb_source *source)
|
||||
{
|
||||
if (source->type != ODB_SOURCE_FILES)
|
||||
BUG("trying to downcast source of type '%d' to files", source->type);
|
||||
BUG("trying to downcast source of type '%s' to '%s'",
|
||||
odb_source_type_to_name(source->type),
|
||||
odb_source_type_to_name(ODB_SOURCE_FILES));
|
||||
return container_of(source, struct odb_source_files, base);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
|
||||
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 in-memory", source->type);
|
||||
BUG("trying to downcast source of type '%s' to '%s'",
|
||||
odb_source_type_to_name(source->type),
|
||||
odb_source_type_to_name(ODB_SOURCE_INMEMORY));
|
||||
return container_of(source, struct odb_source_inmemory, base);
|
||||
}
|
||||
|
||||
|
||||
@@ -1055,5 +1055,7 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb,
|
||||
if (!is_absolute_path(loose->base.path))
|
||||
chdir_notify_register(NULL, odb_source_loose_reparent, loose);
|
||||
|
||||
loose_object_map_load(loose);
|
||||
|
||||
return loose;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb,
|
||||
static inline struct odb_source_loose *odb_source_loose_downcast(struct odb_source *source)
|
||||
{
|
||||
if (source->type != ODB_SOURCE_LOOSE)
|
||||
BUG("trying to downcast source of type '%d' to loose", source->type);
|
||||
BUG("trying to downcast source of type '%s' to '%s'",
|
||||
odb_source_type_to_name(source->type),
|
||||
odb_source_type_to_name(ODB_SOURCE_LOOSE));
|
||||
return container_of(source, struct odb_source_loose, base);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ struct odb_source_packed *odb_source_packed_new(struct object_database *odb,
|
||||
static inline struct odb_source_packed *odb_source_packed_downcast(struct odb_source *source)
|
||||
{
|
||||
if (source->type != ODB_SOURCE_PACKED)
|
||||
BUG("trying to downcast source of type '%d' to packed", source->type);
|
||||
BUG("trying to downcast source of type '%s' to '%s'",
|
||||
odb_source_type_to_name(source->type),
|
||||
odb_source_type_to_name(ODB_SOURCE_PACKED));
|
||||
return container_of(source, struct odb_source_packed, base);
|
||||
}
|
||||
|
||||
|
||||
19
odb/source.c
19
odb/source.c
@@ -4,6 +4,25 @@
|
||||
#include "odb/source.h"
|
||||
#include "packfile.h"
|
||||
|
||||
static const char * const odb_source_names_by_type[] = {
|
||||
[ODB_SOURCE_UNKNOWN] = "unknown",
|
||||
[ODB_SOURCE_FILES] = "files",
|
||||
[ODB_SOURCE_LOOSE] = "loose",
|
||||
[ODB_SOURCE_PACKED] = "packed",
|
||||
[ODB_SOURCE_INMEMORY] = "in-memory",
|
||||
};
|
||||
|
||||
const char *odb_source_type_to_name(enum odb_source_type type)
|
||||
{
|
||||
const char *name;
|
||||
if (type < 0 || type >= ARRAY_SIZE(odb_source_names_by_type))
|
||||
type = ODB_SOURCE_UNKNOWN;
|
||||
name = odb_source_names_by_type[type];
|
||||
if (!name)
|
||||
BUG("name missing in `odb_source_names_by_type` for '%d'", type);
|
||||
return name;
|
||||
}
|
||||
|
||||
struct odb_source *odb_source_new(struct object_database *odb,
|
||||
const char *path,
|
||||
bool local)
|
||||
|
||||
29
odb/source.h
29
odb/source.h
@@ -25,6 +25,12 @@ enum odb_source_type {
|
||||
ODB_SOURCE_INMEMORY,
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert between the enum and its name. Returns the equivalent of "unknown"
|
||||
* for unknown types.
|
||||
*/
|
||||
const char *odb_source_type_to_name(enum odb_source_type type);
|
||||
|
||||
struct object_id;
|
||||
struct odb_read_stream;
|
||||
struct strvec;
|
||||
@@ -83,6 +89,18 @@ struct odb_source {
|
||||
*/
|
||||
void (*close)(struct odb_source *source);
|
||||
|
||||
/*
|
||||
* This callback is expected to create on-disk data structures that are
|
||||
* required for this source to operate.
|
||||
*
|
||||
* The callback is expected to return 0 on success, a negative error
|
||||
* code otherwise.
|
||||
*
|
||||
* This callback may be NULL in case the source does not need any
|
||||
* on-disk setup.
|
||||
*/
|
||||
int (*create_on_disk)(struct odb_source *source);
|
||||
|
||||
/*
|
||||
* This callback is expected to prepare the source so that it becomes
|
||||
* ready for use. It optionally clears underlying caches of the object
|
||||
@@ -327,6 +345,17 @@ static inline void odb_source_close(struct odb_source *source)
|
||||
source->close(source);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create on-disk data structures that are required for this source to operate
|
||||
* correctly. Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static inline int odb_source_create_on_disk(struct odb_source *source)
|
||||
{
|
||||
if (!source->create_on_disk)
|
||||
return 0;
|
||||
return source->create_on_disk(source);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the object database source and clear any caches. Depending on the
|
||||
* backend used this may have the effect that concurrently-written objects
|
||||
|
||||
@@ -201,8 +201,6 @@ void repo_set_compat_hash_algo(struct repository *repo MAYBE_UNUSED, uint32_t al
|
||||
if (hash_algo_by_ptr(repo->hash_algo) == algo)
|
||||
BUG("hash_algo and compat_hash_algo match");
|
||||
repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
|
||||
if (repo->compat_hash_algo)
|
||||
repo_read_loose_object_map(repo);
|
||||
#else
|
||||
if (algo)
|
||||
die(_("compatibility hash algorithm support requires Rust"));
|
||||
@@ -296,6 +294,7 @@ int repo_init(struct repository *repo,
|
||||
warning("%s", err.buf);
|
||||
goto error;
|
||||
}
|
||||
repo->objects = odb_new(repo, 0);
|
||||
|
||||
if (worktree)
|
||||
repo_set_worktree(repo, worktree);
|
||||
|
||||
51
setup.c
51
setup.c
@@ -1765,8 +1765,6 @@ int apply_repository_format(struct repository *repo,
|
||||
enum apply_repository_format_flags flags,
|
||||
struct strbuf *err)
|
||||
{
|
||||
char *object_directory = NULL, *alternate_object_directories = NULL;
|
||||
|
||||
if (verify_repository_format(format, err) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -1779,8 +1777,6 @@ int apply_repository_format(struct repository *repo,
|
||||
if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {
|
||||
const char *shallow_file;
|
||||
|
||||
object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));
|
||||
alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
|
||||
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
|
||||
if (shallow_file)
|
||||
set_alternate_shallow_file(repo, shallow_file);
|
||||
@@ -1788,8 +1784,6 @@ int apply_repository_format(struct repository *repo,
|
||||
|
||||
repo->bare_cfg = format->is_bare;
|
||||
repo_set_hash_algo(repo, format->hash_algo);
|
||||
repo->objects = odb_new(repo, object_directory,
|
||||
alternate_object_directories);
|
||||
repo_set_compat_hash_algo(repo, format->compat_hash_algo);
|
||||
repo_set_ref_storage_format(repo,
|
||||
format->ref_storage_format,
|
||||
@@ -1805,8 +1799,6 @@ int apply_repository_format(struct repository *repo,
|
||||
repo->repository_format_precious_objects =
|
||||
format->precious_objects;
|
||||
|
||||
free(alternate_object_directories);
|
||||
free(object_directory);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1890,6 +1882,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
|
||||
read_and_verify_repository_format(&fmt, ".", NULL);
|
||||
if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
|
||||
die("%s", err.buf);
|
||||
repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV);
|
||||
startup_info->have_repository = 1;
|
||||
|
||||
clear_repository_format(&fmt);
|
||||
@@ -2092,6 +2085,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
|
||||
if (apply_repository_format(repo, &discovery.format,
|
||||
APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
|
||||
die("%s", err.buf);
|
||||
repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV);
|
||||
|
||||
clear_repository_format(&discovery.format);
|
||||
strbuf_release(&err);
|
||||
@@ -2653,25 +2647,29 @@ static int create_default_files(struct repository *repo,
|
||||
return reinit;
|
||||
}
|
||||
|
||||
static void create_object_directory(struct repository *repo)
|
||||
static void create_object_database(struct repository *repo)
|
||||
{
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
size_t baselen;
|
||||
/*
|
||||
* Create the "objects" directory in the common directory. This is done
|
||||
* so that the repository can be discovered regardless of the backend
|
||||
* used.
|
||||
*
|
||||
* Note that we only do this in case the object directory wasn't
|
||||
* overwritten via an environment variable. If it _is_ being overridden
|
||||
* then we skip this step, as the repository won't be discoverable
|
||||
* anyway without the environment variable.
|
||||
*/
|
||||
if (!getenv(DB_ENVIRONMENT)) {
|
||||
struct strbuf objects_dir = STRBUF_INIT;
|
||||
repo_common_path_append(repo, &objects_dir, "objects");
|
||||
safe_create_dir(repo, objects_dir.buf, 1);
|
||||
strbuf_release(&objects_dir);
|
||||
}
|
||||
|
||||
strbuf_addstr(&path, repo_get_object_directory(repo));
|
||||
baselen = path.len;
|
||||
repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV);
|
||||
|
||||
safe_create_dir(repo, path.buf, 1);
|
||||
|
||||
strbuf_setlen(&path, baselen);
|
||||
strbuf_addstr(&path, "/pack");
|
||||
safe_create_dir(repo, path.buf, 1);
|
||||
|
||||
strbuf_setlen(&path, baselen);
|
||||
strbuf_addstr(&path, "/info");
|
||||
safe_create_dir(repo, path.buf, 1);
|
||||
|
||||
strbuf_release(&path);
|
||||
if (odb_source_create_on_disk(repo->objects->sources) < 0)
|
||||
die(_("failed creating object database"));
|
||||
}
|
||||
|
||||
static void separate_git_dir(struct repository *repo,
|
||||
@@ -2869,7 +2867,6 @@ int init_db(struct repository *repo,
|
||||
repository_format_configure(&repo_fmt, hash, ref_storage_format);
|
||||
if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
|
||||
die("%s", err.buf);
|
||||
startup_info->have_repository = 1;
|
||||
|
||||
/*
|
||||
* Ensure `core.hidedotfiles` is processed. This must happen after we
|
||||
@@ -2885,7 +2882,9 @@ int init_db(struct repository *repo,
|
||||
|
||||
if (!(flags & INIT_DB_SKIP_REFDB))
|
||||
create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET);
|
||||
create_object_directory(repo);
|
||||
create_object_database(repo);
|
||||
|
||||
startup_info->have_repository = 1;
|
||||
|
||||
if (repo_settings_get_shared_repository(repo)) {
|
||||
char buf[10];
|
||||
|
||||
4
setup.h
4
setup.h
@@ -245,8 +245,8 @@ enum apply_repository_format_flags {
|
||||
|
||||
/*
|
||||
* Apply the given repository format to the repo. This initializes extensions
|
||||
* and basic data structures required for normal operation. Returns 0 on
|
||||
* success, a negative error code when the format is not valid as determined by
|
||||
* required for normal operation. Returns 0 on success, a negative error code
|
||||
* when the format is not valid as determined by
|
||||
* `verify_repository_format()`.
|
||||
*/
|
||||
int apply_repository_format(struct repository *repo,
|
||||
|
||||
@@ -187,6 +187,24 @@ do
|
||||
eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
|
||||
eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
|
||||
'
|
||||
|
||||
test_expect_success 'rev-parse maps oid of object borrowed from alternate' '
|
||||
for repo in alt borrow
|
||||
do
|
||||
test_when_finished "rm -rf $repo" &&
|
||||
git init --object-format=$hash $repo &&
|
||||
git -C $repo config set core.repositoryformatversion 1 &&
|
||||
git -C $repo config set extensions.compatObjectFormat $(compat_hash $hash) || exit 1
|
||||
done &&
|
||||
|
||||
git -C alt commit --allow-empty --message A &&
|
||||
echo "$(pwd)/alt/.git/objects" >borrow/.git/objects/info/alternates &&
|
||||
|
||||
oid=$(git -C alt rev-parse HEAD) &&
|
||||
git -C alt rev-parse --output-object-format=$(compat_hash $hash) "$oid" >expect &&
|
||||
git -C borrow rev-parse --output-object-format=$(compat_hash $hash) "$oid" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
done
|
||||
cd "$base"
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ static void cl_assert_write_object(struct odb_source_inmemory *source,
|
||||
|
||||
void test_odb_inmemory__initialize(void)
|
||||
{
|
||||
odb = odb_new(&repo, "", "");
|
||||
odb = odb_new(&repo, 0);
|
||||
}
|
||||
|
||||
void test_odb_inmemory__cleanup(void)
|
||||
|
||||
Reference in New Issue
Block a user