aboutsummaryrefslogtreecommitdiff
path: root/odb.c
diff options
context:
space:
mode:
Diffstat (limited to 'odb.c')
-rw-r--r--odb.c626
1 files changed, 365 insertions, 261 deletions
diff --git a/odb.c b/odb.c
index 00a6e71568..9b28fe25ef 100644
--- a/odb.c
+++ b/odb.c
@@ -9,8 +9,10 @@
#include "khash.h"
#include "lockfile.h"
#include "loose.h"
+#include "midx.h"
#include "object-file-convert.h"
#include "object-file.h"
+#include "object-name.h"
#include "odb.h"
#include "packfile.h"
#include "path.h"
@@ -22,6 +24,7 @@
#include "strbuf.h"
#include "strvec.h"
#include "submodule.h"
+#include "tmp-objdir.h"
#include "trace2.h"
#include "write-or-die.h"
@@ -86,18 +89,20 @@ int odb_mkstemp(struct object_database *odb,
/*
* Return non-zero iff the path is usable as an alternate object database.
*/
-static int alt_odb_usable(struct object_database *o,
- struct strbuf *path,
- const char *normalized_objdir, khiter_t *pos)
+static bool odb_is_source_usable(struct object_database *o, const char *path)
{
int r;
+ struct strbuf normalized_objdir = STRBUF_INIT;
+ bool usable = false;
+
+ strbuf_realpath(&normalized_objdir, o->sources->path, 1);
/* Detect cases where alternate disappeared */
- if (!is_directory(path->buf)) {
+ if (!is_directory(path)) {
error(_("object directory %s does not exist; "
"check .git/objects/info/alternates"),
- path->buf);
- return 0;
+ path);
+ goto out;
}
/*
@@ -113,202 +118,133 @@ static int alt_odb_usable(struct object_database *o,
assert(r == 1); /* never used */
kh_value(o->source_by_path, p) = o->sources;
}
- if (fspatheq(path->buf, normalized_objdir))
- return 0;
- *pos = kh_put_odb_path_map(o->source_by_path, path->buf, &r);
- /* r: 0 = exists, 1 = never used, 2 = deleted */
- return r == 0 ? 0 : 1;
-}
-/*
- * Prepare alternate object database registry.
- *
- * The variable alt_odb_list points at the list of struct
- * odb_source. The elements on this list come from
- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
- * whose contents is similar to that environment variable but can be
- * LF separated. Its base points at a statically allocated buffer that
- * contains "/the/directory/corresponding/to/.git/objects/...", while
- * its name points just after the slash at the end of ".git/objects/"
- * in the example above, and has enough space to hold all hex characters
- * of the object ID, an extra slash for the first level indirection, and
- * the terminating NUL.
- */
-static void read_info_alternates(struct object_database *odb,
- const char *relative_base,
- int depth);
+ if (fspatheq(path, normalized_objdir.buf))
+ goto out;
-static struct odb_source *link_alt_odb_entry(struct object_database *odb,
- const char *dir,
- const char *relative_base,
- int depth)
-{
- struct odb_source *alternate = NULL;
- struct strbuf pathbuf = STRBUF_INIT;
- struct strbuf tmp = STRBUF_INIT;
- khiter_t pos;
+ if (kh_get_odb_path_map(o->source_by_path, path) < kh_end(o->source_by_path))
+ goto out;
- if (!is_absolute_path(dir) && relative_base) {
- strbuf_realpath(&pathbuf, relative_base, 1);
- strbuf_addch(&pathbuf, '/');
- }
- strbuf_addstr(&pathbuf, dir);
+ usable = true;
- if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
- error(_("unable to normalize alternate object path: %s"),
- pathbuf.buf);
- goto error;
- }
- strbuf_swap(&pathbuf, &tmp);
+out:
+ strbuf_release(&normalized_objdir);
+ return usable;
+}
- /*
- * The trailing slash after the directory name is given by
- * this function at the end. Remove duplicates.
- */
- while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
- strbuf_setlen(&pathbuf, pathbuf.len - 1);
+void parse_alternates(const char *string,
+ int sep,
+ const char *relative_base,
+ struct strvec *out)
+{
+ struct strbuf pathbuf = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
- strbuf_reset(&tmp);
- strbuf_realpath(&tmp, odb->sources->path, 1);
+ if (!string || !*string)
+ return;
- if (!alt_odb_usable(odb, &pathbuf, tmp.buf, &pos))
- goto error;
+ while (*string) {
+ const char *end;
- CALLOC_ARRAY(alternate, 1);
- alternate->odb = odb;
- alternate->local = false;
- /* pathbuf.buf is already in r->objects->source_by_path */
- alternate->path = strbuf_detach(&pathbuf, NULL);
+ strbuf_reset(&buf);
+ strbuf_reset(&pathbuf);
- /* add the alternate entry */
- *odb->sources_tail = alternate;
- odb->sources_tail = &(alternate->next);
- alternate->next = NULL;
- assert(odb->source_by_path);
- kh_value(odb->source_by_path, pos) = alternate;
+ if (*string == '#') {
+ /* comment; consume up to next separator */
+ end = strchrnul(string, sep);
+ } else if (*string == '"' && !unquote_c_style(&buf, string, &end)) {
+ /*
+ * quoted path; unquote_c_style has copied the
+ * data for us and set "end". Broken quoting (e.g.,
+ * an entry that doesn't end with a quote) falls
+ * back to the unquoted case below.
+ */
+ } else {
+ /* normal, unquoted path */
+ end = strchrnul(string, sep);
+ strbuf_add(&buf, string, end - string);
+ }
- /* recursively add alternates */
- read_info_alternates(odb, alternate->path, depth + 1);
+ if (*end)
+ end++;
+ string = end;
- error:
- strbuf_release(&tmp);
- strbuf_release(&pathbuf);
- return alternate;
-}
+ if (!buf.len)
+ continue;
-static const char *parse_alt_odb_entry(const char *string,
- int sep,
- struct strbuf *out)
-{
- const char *end;
+ if (!is_absolute_path(buf.buf) && relative_base) {
+ strbuf_realpath(&pathbuf, relative_base, 1);
+ strbuf_addch(&pathbuf, '/');
+ }
+ strbuf_addbuf(&pathbuf, &buf);
- strbuf_reset(out);
+ strbuf_reset(&buf);
+ if (!strbuf_realpath(&buf, pathbuf.buf, 0)) {
+ error(_("unable to normalize alternate object path: %s"),
+ pathbuf.buf);
+ continue;
+ }
- if (*string == '#') {
- /* comment; consume up to next separator */
- end = strchrnul(string, sep);
- } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
/*
- * quoted path; unquote_c_style has copied the
- * data for us and set "end". Broken quoting (e.g.,
- * an entry that doesn't end with a quote) falls
- * back to the unquoted case below.
+ * The trailing slash after the directory name is given by
+ * this function at the end. Remove duplicates.
*/
- } else {
- /* normal, unquoted path */
- end = strchrnul(string, sep);
- strbuf_add(out, string, end - string);
+ while (buf.len && buf.buf[buf.len - 1] == '/')
+ strbuf_setlen(&buf, buf.len - 1);
+
+ strvec_push(out, buf.buf);
}
- if (*end)
- end++;
- return end;
+ strbuf_release(&pathbuf);
+ strbuf_release(&buf);
}
-static void link_alt_odb_entries(struct object_database *odb, const char *alt,
- int sep, const char *relative_base, int depth)
+static struct odb_source *odb_add_alternate_recursively(struct object_database *odb,
+ const char *source,
+ int depth)
{
- struct strbuf dir = STRBUF_INIT;
+ struct odb_source *alternate = NULL;
+ struct strvec sources = STRVEC_INIT;
+ khiter_t pos;
+ int ret;
- if (!alt || !*alt)
- return;
+ if (!odb_is_source_usable(odb, source))
+ goto error;
- if (depth > 5) {
- error(_("%s: ignoring alternate object stores, nesting too deep"),
- relative_base);
- return;
- }
+ alternate = odb_source_new(odb, source, false);
- while (*alt) {
- alt = parse_alt_odb_entry(alt, sep, &dir);
- if (!dir.len)
- continue;
- link_alt_odb_entry(odb, dir.buf, relative_base, depth);
- }
- strbuf_release(&dir);
-}
+ /* add the alternate entry */
+ *odb->sources_tail = alternate;
+ odb->sources_tail = &(alternate->next);
-static void read_info_alternates(struct object_database *odb,
- const char *relative_base,
- int depth)
-{
- char *path;
- struct strbuf buf = STRBUF_INIT;
+ pos = kh_put_odb_path_map(odb->source_by_path, alternate->path, &ret);
+ if (!ret)
+ BUG("source must not yet exist");
+ kh_value(odb->source_by_path, pos) = alternate;
- path = xstrfmt("%s/info/alternates", relative_base);
- if (strbuf_read_file(&buf, path, 1024) < 0) {
- warn_on_fopen_errors(path);
- free(path);
- return;
+ /* recursively add alternates */
+ odb_source_read_alternates(alternate, &sources);
+ if (sources.nr && depth + 1 > 5) {
+ error(_("%s: ignoring alternate object stores, nesting too deep"),
+ source);
+ } else {
+ for (size_t i = 0; i < sources.nr; i++)
+ odb_add_alternate_recursively(odb, sources.v[i], depth + 1);
}
- link_alt_odb_entries(odb, buf.buf, '\n', relative_base, depth);
- strbuf_release(&buf);
- free(path);
+ error:
+ strvec_clear(&sources);
+ return alternate;
}
void odb_add_to_alternates_file(struct object_database *odb,
const char *dir)
{
- struct lock_file lock = LOCK_INIT;
- char *alts = repo_git_path(odb->repo, "objects/info/alternates");
- FILE *in, *out;
- int found = 0;
-
- hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
- out = fdopen_lock_file(&lock, "w");
- if (!out)
- die_errno(_("unable to fdopen alternates lockfile"));
-
- in = fopen(alts, "r");
- if (in) {
- struct strbuf line = STRBUF_INIT;
-
- while (strbuf_getline(&line, in) != EOF) {
- if (!strcmp(dir, line.buf)) {
- found = 1;
- break;
- }
- fprintf_or_die(out, "%s\n", line.buf);
- }
-
- strbuf_release(&line);
- fclose(in);
- }
- else if (errno != ENOENT)
- die_errno(_("unable to read alternates file"));
-
- if (found) {
- rollback_lock_file(&lock);
- } else {
- fprintf_or_die(out, "%s\n", dir);
- if (commit_lock_file(&lock))
- die_errno(_("unable to move new alternates file into place"));
- if (odb->loaded_alternates)
- link_alt_odb_entries(odb, dir, '\n', NULL, 0);
- }
- free(alts);
+ int ret = odb_source_write_alternate(odb->sources, dir);
+ if (ret < 0)
+ die(NULL);
+ if (odb->loaded_alternates)
+ odb_add_alternate_recursively(odb, dir, 0);
}
struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
@@ -319,7 +255,7 @@ struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
* overwritten when they are.
*/
odb_prepare_alternates(odb);
- return link_alt_odb_entry(odb, dir, NULL, 0);
+ return odb_add_alternate_recursively(odb, dir, 0);
}
struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
@@ -337,29 +273,19 @@ struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
* Make a new primary odb and link the old primary ODB in as an
* alternate
*/
- source = xcalloc(1, sizeof(*source));
- source->odb = odb;
- source->path = xstrdup(dir);
+ source = odb_source_new(odb, dir, false);
/*
* Disable ref updates while a temporary odb is active, since
* the objects in the database may roll back.
*/
- source->disable_ref_updates = 1;
+ odb->repo->disable_ref_updates = true;
source->will_destroy = will_destroy;
source->next = odb->sources;
odb->sources = source;
return source->next;
}
-static void free_object_directory(struct odb_source *source)
-{
- free(source->path);
- odb_clear_loose_cache(source);
- loose_object_map_clear(&source->loose_map);
- free(source);
-}
-
void odb_restore_primary_source(struct object_database *odb,
struct odb_source *restore_source,
const char *old_path)
@@ -373,8 +299,9 @@ void odb_restore_primary_source(struct object_database *odb,
if (cur_source->next != restore_source)
BUG("we expect the old primary object store to be the first alternate");
+ odb->repo->disable_ref_updates = false;
odb->sources = restore_source;
- free_object_directory(cur_source);
+ odb_source_free(cur_source);
}
char *compute_alternate_path(const char *path, struct strbuf *err)
@@ -592,13 +519,19 @@ int odb_for_each_alternate(struct object_database *odb,
void odb_prepare_alternates(struct object_database *odb)
{
+ struct strvec sources = STRVEC_INIT;
+
if (odb->loaded_alternates)
return;
- link_alt_odb_entries(odb, odb->alternate_db, PATH_SEP, NULL, 0);
+ parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources);
+ odb_source_read_alternates(odb->sources, &sources);
+ for (size_t i = 0; i < sources.nr; i++)
+ odb_add_alternate_recursively(odb, sources.v[i], 0);
- read_info_alternates(odb, odb->sources->path, 0);
odb->loaded_alternates = 1;
+
+ strvec_clear(&sources);
}
int odb_has_alternates(struct object_database *odb)
@@ -651,52 +584,55 @@ static int do_oid_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi, unsigned flags)
{
- static struct object_info blank_oi = OBJECT_INFO_INIT;
const struct cached_object *co;
- struct pack_entry e;
- int rtype;
const struct object_id *real = oid;
int already_retried = 0;
-
if (flags & OBJECT_INFO_LOOKUP_REPLACE)
real = lookup_replace_object(odb->repo, oid);
if (is_null_oid(real))
return -1;
- if (!oi)
- oi = &blank_oi;
-
co = find_cached_object(odb, real);
if (co) {
- if (oi->typep)
- *(oi->typep) = co->type;
- if (oi->sizep)
- *(oi->sizep) = co->size;
- if (oi->disk_sizep)
- *(oi->disk_sizep) = 0;
- if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid, odb->repo->hash_algo);
- if (oi->contentp)
- *oi->contentp = xmemdupz(co->buf, co->size);
- oi->whence = OI_CACHED;
+ if (oi) {
+ if (oi->typep)
+ *(oi->typep) = co->type;
+ if (oi->sizep)
+ *(oi->sizep) = co->size;
+ if (oi->disk_sizep)
+ *(oi->disk_sizep) = 0;
+ if (oi->delta_base_oid)
+ oidclr(oi->delta_base_oid, odb->repo->hash_algo);
+ if (oi->contentp)
+ *oi->contentp = xmemdupz(co->buf, co->size);
+ if (oi->mtimep)
+ *oi->mtimep = 0;
+ oi->whence = OI_CACHED;
+ }
return 0;
}
+ odb_prepare_alternates(odb);
+
while (1) {
- if (find_pack_entry(odb->repo, real, &e))
- break;
+ struct odb_source *source;
- /* Most likely it's a loose object. */
- if (!loose_object_info(odb->repo, real, oi, flags))
- return 0;
+ for (source = odb->sources; source; source = source->next)
+ if (!odb_source_read_object_info(source, real, oi, flags))
+ return 0;
- /* Not a loose object; someone else may have just packed it. */
+ /*
+ * When the object hasn't been found we try a second read and
+ * tell the sources so. This may cause them to invalidate
+ * caches or reload on-disk state.
+ */
if (!(flags & OBJECT_INFO_QUICK)) {
- odb_reprepare(odb->repo->objects);
- if (find_pack_entry(odb->repo, real, &e))
- break;
+ for (source = odb->sources; source; source = source->next)
+ if (!odb_source_read_object_info(source, real, oi,
+ flags | OBJECT_INFO_SECOND_READ))
+ return 0;
}
/*
@@ -729,25 +665,6 @@ static int do_oid_object_info_extended(struct object_database *odb,
}
return -1;
}
-
- if (oi == &blank_oi)
- /*
- * We know that the caller doesn't actually need the
- * information below, so return early.
- */
- return 0;
- rtype = packed_object_info(odb->repo, e.p, e.offset, oi);
- if (rtype < 0) {
- mark_bad_packed_object(e.p, real);
- return do_oid_object_info_extended(odb, real, oi, 0);
- } else if (oi->whence == OI_PACKED) {
- oi->u.packed.offset = e.offset;
- oi->u.packed.pack = e.p;
- oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
- rtype == OBJ_OFS_DELTA);
- }
-
- return 0;
}
static int oid_object_info_convert(struct repository *r,
@@ -833,7 +750,7 @@ static int oid_object_info_convert(struct repository *r,
int odb_read_object_info_extended(struct object_database *odb,
const struct object_id *oid,
struct object_info *oi,
- unsigned flags)
+ enum object_info_flags flags)
{
int ret;
@@ -955,20 +872,171 @@ void *odb_read_object_peeled(struct object_database *odb,
}
int odb_has_object(struct object_database *odb, const struct object_id *oid,
- unsigned flags)
+ enum odb_has_object_flags flags)
{
unsigned object_info_flags = 0;
if (!startup_info->have_repository)
return 0;
- if (!(flags & HAS_OBJECT_RECHECK_PACKED))
+ if (!(flags & ODB_HAS_OBJECT_RECHECK_PACKED))
object_info_flags |= OBJECT_INFO_QUICK;
- if (!(flags & HAS_OBJECT_FETCH_PROMISOR))
+ if (!(flags & ODB_HAS_OBJECT_FETCH_PROMISOR))
object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT;
return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0;
}
+int odb_freshen_object(struct object_database *odb,
+ const struct object_id *oid)
+{
+ struct odb_source *source;
+ odb_prepare_alternates(odb);
+ for (source = odb->sources; source; source = source->next)
+ if (odb_source_freshen_object(source, oid))
+ return 1;
+ return 0;
+}
+
+int odb_for_each_object_ext(struct object_database *odb,
+ const struct object_info *request,
+ odb_for_each_object_cb cb,
+ void *cb_data,
+ const struct odb_for_each_object_options *opts)
+{
+ int ret;
+
+ odb_prepare_alternates(odb);
+ for (struct odb_source *source = odb->sources; source; source = source->next) {
+ if (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
+ continue;
+
+ ret = odb_source_for_each_object(source, request, cb, cb_data, opts);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int odb_for_each_object(struct object_database *odb,
+ const struct object_info *request,
+ odb_for_each_object_cb cb,
+ void *cb_data,
+ enum odb_for_each_object_flags flags)
+{
+ struct odb_for_each_object_options opts = {
+ .flags = flags,
+ };
+ return odb_for_each_object_ext(odb, request, cb, cb_data, &opts);
+}
+
+int odb_count_objects(struct object_database *odb,
+ enum odb_count_objects_flags flags,
+ unsigned long *out)
+{
+ struct odb_source *source;
+ unsigned long count = 0;
+ int ret;
+
+ if (odb->object_count_valid && odb->object_count_flags == flags) {
+ *out = odb->object_count;
+ return 0;
+ }
+
+ odb_prepare_alternates(odb);
+ for (source = odb->sources; source; source = source->next) {
+ unsigned long c;
+
+ ret = odb_source_count_objects(source, flags, &c);
+ if (ret < 0)
+ goto out;
+
+ count += c;
+ }
+
+ odb->object_count = count;
+ odb->object_count_valid = 1;
+ odb->object_count_flags = flags;
+
+ *out = count;
+ ret = 0;
+
+out:
+ return ret;
+}
+
+/*
+ * Return the slot of the most-significant bit set in "val". There are various
+ * ways to do this quickly with fls() or __builtin_clzl(), but speed is
+ * probably not a big deal here.
+ */
+static unsigned msb(unsigned long val)
+{
+ unsigned r = 0;
+ while (val >>= 1)
+ r++;
+ return r;
+}
+
+int odb_find_abbrev_len(struct object_database *odb,
+ const struct object_id *oid,
+ int min_length,
+ unsigned *out)
+{
+ const struct git_hash_algo *algo =
+ oid->algo ? &hash_algos[oid->algo] : odb->repo->hash_algo;
+ const unsigned hexsz = algo->hexsz;
+ unsigned len;
+ int ret;
+
+ if (min_length < 0) {
+ unsigned long count;
+
+ if (odb_count_objects(odb, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
+ count = 0;
+
+ /*
+ * Add one because the MSB only tells us the highest bit set,
+ * not including the value of all the _other_ bits (so "15"
+ * is only one off of 2^4, but the MSB is the 3rd bit.
+ */
+ len = msb(count) + 1;
+ /*
+ * We now know we have on the order of 2^len objects, which
+ * expects a collision at 2^(len/2). But we also care about hex
+ * chars, not bits, and there are 4 bits per hex. So all
+ * together we need to divide by 2 and round up.
+ */
+ len = DIV_ROUND_UP(len, 2);
+ /*
+ * For very small repos, we stick with our regular fallback.
+ */
+ if (len < FALLBACK_DEFAULT_ABBREV)
+ len = FALLBACK_DEFAULT_ABBREV;
+ } else {
+ len = min_length;
+ }
+
+ if (len >= hexsz || !len) {
+ *out = hexsz;
+ ret = 0;
+ goto out;
+ }
+
+ odb_prepare_alternates(odb);
+ for (struct odb_source *source = odb->sources; source; source = source->next) {
+ ret = odb_source_find_abbrev_len(source, oid, len, &len);
+ if (ret)
+ goto out;
+ }
+
+ ret = 0;
+ *out = len;
+
+out:
+ return ret;
+}
+
void odb_assert_oid_type(struct object_database *odb,
const struct object_id *oid, enum object_type expect)
{
@@ -985,60 +1053,83 @@ int odb_write_object_ext(struct object_database *odb,
enum object_type type,
struct object_id *oid,
struct object_id *compat_oid,
- unsigned flags)
+ enum odb_write_object_flags flags)
{
- return write_object_file(odb->sources, buf, len, type, oid, compat_oid, flags);
+ return odb_source_write_object(odb->sources, buf, len, type,
+ oid, compat_oid, flags);
}
-struct object_database *odb_new(struct repository *repo)
+int odb_write_object_stream(struct object_database *odb,
+ struct odb_write_stream *stream, size_t len,
+ struct object_id *oid)
+{
+ return odb_source_write_object_stream(odb->sources, stream, len, oid);
+}
+
+struct object_database *odb_new(struct repository *repo,
+ const char *primary_source,
+ const char *secondary_sources)
{
struct object_database *o = xmalloc(sizeof(*o));
+ char *to_free = NULL;
memset(o, 0, sizeof(*o));
o->repo = repo;
- o->packfiles = packfile_store_new(o);
pthread_mutex_init(&o->replace_mutex, NULL);
string_list_init_dup(&o->submodule_source_paths);
+
+ if (!primary_source)
+ primary_source = to_free = 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);
+
+ free(to_free);
+
return o;
}
-static void free_object_directories(struct object_database *o)
+void odb_close(struct object_database *o)
+{
+ struct odb_source *source;
+ for (source = o->sources; source; source = source->next)
+ odb_source_close(source);
+ close_commit_graph(o);
+}
+
+static void odb_free_sources(struct object_database *o)
{
while (o->sources) {
struct odb_source *next;
next = o->sources->next;
- free_object_directory(o->sources);
+ odb_source_free(o->sources);
o->sources = next;
}
kh_destroy_odb_path_map(o->source_by_path);
o->source_by_path = NULL;
}
-void odb_clear(struct object_database *o)
+void odb_free(struct object_database *o)
{
- FREE_AND_NULL(o->alternate_db);
+ if (!o)
+ return;
+
+ free(o->alternate_db);
oidmap_clear(&o->replace_map, 1);
pthread_mutex_destroy(&o->replace_mutex);
- free_commit_graph(o->commit_graph);
- o->commit_graph = NULL;
- o->commit_graph_attempted = 0;
-
- free_object_directories(o);
- o->sources_tail = NULL;
- o->loaded_alternates = 0;
+ 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_AND_NULL(o->cached_objects);
-
- close_object_store(o);
- packfile_store_free(o->packfiles);
- o->packfiles = NULL;
+ free(o->cached_objects);
string_list_clear(&o->submodule_source_paths, 0);
+
+ free(o);
}
void odb_reprepare(struct object_database *o)
@@ -1057,21 +1148,34 @@ void odb_reprepare(struct object_database *o)
odb_prepare_alternates(o);
for (source = o->sources; source; source = source->next)
- odb_clear_loose_cache(source);
+ odb_source_reprepare(source);
- o->approximate_object_count_valid = 0;
-
- packfile_store_reprepare(o->packfiles);
+ o->object_count_valid = 0;
obj_read_unlock();
}
struct odb_transaction *odb_transaction_begin(struct object_database *odb)
{
- return object_file_transaction_begin(odb->sources);
+ if (odb->transaction)
+ return NULL;
+
+ odb->transaction = odb_transaction_files_begin(odb->sources);
+
+ return odb->transaction;
}
void odb_transaction_commit(struct odb_transaction *transaction)
{
- object_file_transaction_commit(transaction);
+ if (!transaction)
+ return;
+
+ /*
+ * Ensure the transaction ending matches the pending transaction.
+ */
+ ASSERT(transaction == transaction->source->odb->transaction);
+
+ transaction->commit(transaction);
+ transaction->source->odb->transaction = NULL;
+ free(transaction);
}