Merge branch 'ps/odb-make-creation-pluggable' into ps/odb-eagerly-load-alternates
* 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 sourcemain
commit
d296c52baa
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue