summaryrefslogtreecommitdiff
path: root/src/odb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/odb.c')
-rw-r--r--src/odb.c216
1 files changed, 157 insertions, 59 deletions
diff --git a/src/odb.c b/src/odb.c
index c98df247c..23eb4e12e 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -8,11 +8,13 @@
#include "common.h"
#include <zlib.h>
#include "git2/object.h"
+#include "git2/sys/odb_backend.h"
#include "fileops.h"
#include "hash.h"
#include "odb.h"
#include "delta-apply.h"
#include "filter.h"
+#include "repository.h"
#include "git2/odb_backend.h"
#include "git2/oid.h"
@@ -29,10 +31,19 @@ typedef struct
{
git_odb_backend *backend;
int priority;
- int is_alternate;
+ bool is_alternate;
+ ino_t disk_inode;
} backend_internal;
-size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE;
+static git_cache *odb_cache(git_odb *odb)
+{
+ if (odb->rc.owner != NULL) {
+ git_repository *owner = odb->rc.owner;
+ return &owner->objects;
+ }
+
+ return &odb->own_cache;
+}
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
@@ -54,6 +65,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
if (!git_object_typeisloose(obj->type))
return -1;
+
if (!obj->data && obj->len != 0)
return -1;
@@ -70,23 +82,24 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
}
-static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)
+static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source)
{
- git_odb_object *object = git__malloc(sizeof(git_odb_object));
- memset(object, 0x0, sizeof(git_odb_object));
+ git_odb_object *object = git__calloc(1, sizeof(git_odb_object));
- git_oid_cpy(&object->cached.oid, oid);
- memcpy(&object->raw, source, sizeof(git_rawobj));
+ if (object != NULL) {
+ git_oid_cpy(&object->cached.oid, oid);
+ object->cached.type = source->type;
+ object->cached.size = source->len;
+ object->buffer = source->data;
+ }
return object;
}
-static void free_odb_object(void *o)
+void git_odb_object__free(void *object)
{
- git_odb_object *object = (git_odb_object *)o;
-
if (object != NULL) {
- git__free(object->raw.data);
+ git__free(((git_odb_object *)object)->buffer);
git__free(object);
}
}
@@ -98,17 +111,17 @@ const git_oid *git_odb_object_id(git_odb_object *object)
const void *git_odb_object_data(git_odb_object *object)
{
- return object->raw.data;
+ return object->buffer;
}
size_t git_odb_object_size(git_odb_object *object)
{
- return object->raw.len;
+ return object->cached.size;
}
git_otype git_odb_object_type(git_odb_object *object)
{
- return object->raw.type;
+ return object->cached.type;
}
void git_odb_object_free(git_odb_object *object)
@@ -116,7 +129,7 @@ void git_odb_object_free(git_odb_object *object)
if (object == NULL)
return;
- git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
+ git_cached_obj_decref(object);
}
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
@@ -219,6 +232,7 @@ int git_odb__hashlink(git_oid *out, const char *path)
link_data[size] = '\0';
if (read_len != (ssize_t)size) {
giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+ git__free(link_data);
return -1;
}
@@ -353,9 +367,8 @@ int git_odb_new(git_odb **out)
git_odb *db = git__calloc(1, sizeof(*db));
GITERR_CHECK_ALLOC(db);
- if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 ||
- git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
- {
+ if (git_cache_init(&db->own_cache) < 0 ||
+ git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
git__free(db);
return -1;
}
@@ -365,7 +378,9 @@ int git_odb_new(git_odb **out)
return 0;
}
-static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
+static int add_backend_internal(
+ git_odb *odb, git_odb_backend *backend,
+ int priority, bool is_alternate, ino_t disk_inode)
{
backend_internal *internal;
@@ -382,6 +397,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
internal->backend = backend;
internal->priority = priority;
internal->is_alternate = is_alternate;
+ internal->disk_inode = disk_inode;
if (git_vector_insert(&odb->backends, internal) < 0) {
git__free(internal);
@@ -395,26 +411,86 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
{
- return add_backend_internal(odb, backend, priority, 0);
+ return add_backend_internal(odb, backend, priority, false, 0);
}
int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
{
- return add_backend_internal(odb, backend, priority, 1);
+ return add_backend_internal(odb, backend, priority, true, 0);
+}
+
+size_t git_odb_num_backends(git_odb *odb)
+{
+ assert(odb);
+ return odb->backends.length;
+}
+
+static int git_odb__error_unsupported_in_backend(const char *action)
+{
+ giterr_set(GITERR_ODB,
+ "Cannot %s - unsupported in the loaded odb backends", action);
+ return -1;
+}
+
+
+int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
+{
+ backend_internal *internal;
+
+ assert(odb && odb);
+ internal = git_vector_get(&odb->backends, pos);
+
+ if (internal && internal->backend) {
+ *out = internal->backend;
+ return 0;
+ }
+
+ giterr_set(GITERR_ODB, "No ODB backend loaded at index " PRIuZ, pos);
+ return GIT_ENOTFOUND;
}
-static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth)
+static int add_default_backends(
+ git_odb *db, const char *objects_dir,
+ bool as_alternates, int alternate_depth)
{
+ size_t i;
+ struct stat st;
+ ino_t inode;
git_odb_backend *loose, *packed;
+ /* TODO: inodes are not really relevant on Win32, so we need to find
+ * a cross-platform workaround for this */
+#ifdef GIT_WIN32
+ GIT_UNUSED(i);
+ GIT_UNUSED(st);
+
+ inode = 0;
+#else
+ if (p_stat(objects_dir, &st) < 0) {
+ if (as_alternates)
+ return 0;
+
+ giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir);
+ return -1;
+ }
+
+ inode = st.st_ino;
+
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *backend = git_vector_get(&db->backends, i);
+ if (backend->disk_inode == inode)
+ return 0;
+ }
+#endif
+
/* add the loose object backend */
if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
- add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
+ add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
return -1;
/* add the packed file backend */
if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
+ add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0)
return -1;
return load_alternates(db, objects_dir, alternate_depth);
@@ -429,9 +505,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_
int result = 0;
/* Git reports an error, we just ignore anything deeper */
- if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) {
+ if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH)
return 0;
- }
if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
return -1;
@@ -464,7 +539,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_
alternate = git_buf_cstr(&alternates_path);
}
- if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0)
+ if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
break;
}
@@ -476,7 +551,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_
int git_odb_add_disk_alternate(git_odb *odb, const char *path)
{
- return add_default_backends(odb, path, 1, 0);
+ return add_default_backends(odb, path, true, 0);
}
int git_odb_open(git_odb **out, const char *objects_dir)
@@ -514,7 +589,9 @@ static void odb_free(git_odb *db)
}
git_vector_free(&db->backends);
- git_cache_free(&db->cache);
+ git_cache_free(&db->own_cache);
+
+ git__memzero(db, sizeof(*db));
git__free(db);
}
@@ -535,7 +612,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
assert(db && id);
- if ((object = git_cache_get(&db->cache, id)) != NULL) {
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
return (int)true;
}
@@ -585,9 +662,9 @@ int git_odb__read_header_or_object(
assert(db && id && out && len_p && type_p);
- if ((object = git_cache_get(&db->cache, id)) != NULL) {
- *len_p = object->raw.len;
- *type_p = object->raw.type;
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
*out = object;
return 0;
}
@@ -612,8 +689,8 @@ int git_odb__read_header_or_object(
if ((error = git_odb_read(&object, db, id)) < 0)
return error; /* error already set - pass along */
- *len_p = object->raw.len;
- *type_p = object->raw.type;
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
*out = object;
return 0;
@@ -621,19 +698,15 @@ int git_odb__read_header_or_object(
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
{
- size_t i;
+ size_t i, reads = 0;
int error;
bool refreshed = false;
git_rawobj raw;
+ git_odb_object *object;
assert(out && db && id);
- if (db->backends.length == 0) {
- giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded");
- return GIT_ENOTFOUND;
- }
-
- *out = git_cache_get(&db->cache, id);
+ *out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
@@ -644,8 +717,10 @@ attempt_lookup:
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
- if (b->read != NULL)
+ if (b->read != NULL) {
+ ++reads;
error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ }
}
if (error == GIT_ENOTFOUND && !refreshed) {
@@ -656,10 +731,16 @@ attempt_lookup:
goto attempt_lookup;
}
- if (error && error != GIT_PASSTHROUGH)
+ if (error && error != GIT_PASSTHROUGH) {
+ if (!reads)
+ return git_odb__error_notfound("no match for id", id);
return error;
+ }
+
+ if ((object = odb_object__alloc(id, &raw)) == NULL)
+ return -1;
- *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
+ *out = git_cache_store_raw(odb_cache(db), object);
return 0;
}
@@ -672,6 +753,7 @@ int git_odb_read_prefix(
git_rawobj raw;
void *data = NULL;
bool found = false, refreshed = false;
+ git_odb_object *object;
assert(out && db);
@@ -682,7 +764,7 @@ int git_odb_read_prefix(
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
- *out = git_cache_get(&db->cache, short_id);
+ *out = git_cache_get_raw(odb_cache(db), short_id);
if (*out != NULL)
return 0;
}
@@ -704,7 +786,7 @@ attempt_lookup:
git__free(data);
data = raw.data;
- if (found && git_oid_cmp(&full_oid, &found_full_oid))
+ if (found && git_oid__cmp(&full_oid, &found_full_oid))
return git_odb__error_ambiguous("multiple matches for prefix");
found_full_oid = full_oid;
@@ -723,7 +805,10 @@ attempt_lookup:
if (!found)
return git_odb__error_notfound("no match for prefix", short_id);
- *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
+ if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
+ return -1;
+
+ *out = git_cache_store_raw(odb_cache(db), object);
return 0;
}
@@ -770,10 +855,10 @@ int git_odb_write(
if (!error || error == GIT_PASSTHROUGH)
return 0;
- /* if no backends were able to write the object directly, we try a streaming
- * write to the backends; just write the whole object into the stream in one
- * push */
-
+ /* if no backends were able to write the object directly, we try a
+ * streaming write to the backends; just write the whole object into the
+ * stream in one push
+ */
if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
return error;
@@ -787,7 +872,7 @@ int git_odb_write(
int git_odb_open_wstream(
git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
{
- size_t i;
+ size_t i, writes = 0;
int error = GIT_ERROR;
assert(stream && db);
@@ -800,21 +885,26 @@ int git_odb_open_wstream(
if (internal->is_alternate)
continue;
- if (b->writestream != NULL)
+ if (b->writestream != NULL) {
+ ++writes;
error = b->writestream(stream, b, size, type);
- else if (b->write != NULL)
+ } else if (b->write != NULL) {
+ ++writes;
error = init_fake_wstream(stream, b, size, type);
+ }
}
if (error == GIT_PASSTHROUGH)
error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write object");
return error;
}
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
{
- size_t i;
+ size_t i, reads = 0;
int error = GIT_ERROR;
assert(stream && db);
@@ -823,19 +913,23 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
- if (b->readstream != NULL)
+ if (b->readstream != NULL) {
+ ++reads;
error = b->readstream(stream, b, oid);
+ }
}
if (error == GIT_PASSTHROUGH)
error = 0;
+ if (error < 0 && !reads)
+ error = git_odb__error_unsupported_in_backend("read object streamed");
return error;
}
int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload)
{
- size_t i;
+ size_t i, writes = 0;
int error = GIT_ERROR;
assert(out && db);
@@ -848,12 +942,16 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer
if (internal->is_alternate)
continue;
- if (b->writepack != NULL)
+ if (b->writepack != NULL) {
+ ++writes;
error = b->writepack(out, b, progress_cb, progress_payload);
+ }
}
if (error == GIT_PASSTHROUGH)
error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write pack");
return error;
}