backend->parent.exists = &sqlite_backend__exists;
backend->parent.free = &sqlite_backend__free;
- backend->parent.priority = 0;
-
*backend_out = (git_odb_backend *)backend;
return GIT_SUCCESS;
#ifndef INCLUDE_git_git_h__
#define INCLUDE_git_git_h__
-#define LIBGIT2_VERSION "0.3.0"
+#define LIBGIT2_VERSION "0.4.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 3
+#define LIBGIT2_VER_MINOR 4
#define LIBGIT2_VER_REVISION 0
#include "git2/common.h"
* @paramm backend pointer to a git_odb_backend instance
* @return 0 on sucess; error code otherwise
*/
-GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend);
+GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
+
+/**
+ * Add a custom backend to an existing Object DB; this
+ * backend will work as an alternate.
+ *
+ * Alternate backends are always checked for objects *after*
+ * all the main backends have been exhausted.
+ *
+ * Writing is disabled on alternate backends.
+ *
+ * Read <odb_backends.h> for more information.
+ *
+ * @param odb database to add the backend to
+ * @paramm backend pointer to a git_odb_backend instance
+ * @return 0 on sucess; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
/**
* Close an open object database.
struct git_odb_backend {
git_odb *odb;
- int priority;
-
int (* read)(
git_rawobj *,
struct git_odb_backend *,
#define GIT_ALTERNATES_FILE "info/alternates"
+/* TODO: is this correct? */
+#define GIT_LOOSE_PRIORITY 2
+#define GIT_PACKED_PRIORITY 1
+
+typedef struct
+{
+ git_odb_backend *backend;
+ int priority;
+ int is_alternate;
+} backend_internal;
+
static int format_object_header(char *hdr, size_t n, git_rawobj *obj)
{
const char *type_str = git_object_type2string(obj->type);
int backend_sort_cmp(const void *a, const void *b)
{
- const git_odb_backend *backend_a = *(const git_odb_backend **)(a);
- const git_odb_backend *backend_b = *(const git_odb_backend **)(b);
+ const backend_internal *backend_a = *(const backend_internal **)(a);
+ const backend_internal *backend_b = *(const backend_internal **)(b);
+
+ if (backend_a->is_alternate == backend_b->is_alternate)
+ return (backend_b->priority - backend_a->priority);
- return (backend_b->priority - backend_a->priority);
+ return backend_a->is_alternate ? 1 : -1;
}
int git_odb_new(git_odb **out)
return GIT_SUCCESS;
}
-int git_odb_add_backend(git_odb *odb, git_odb_backend *backend)
+static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
{
+ backend_internal *internal;
+
assert(odb && backend);
if (backend->odb != NULL && backend->odb != odb)
return GIT_EBUSY;
- backend->odb = odb;
+ internal = git__malloc(sizeof(backend_internal));
+ if (internal == NULL)
+ return GIT_ENOMEM;
+
+ internal->backend = backend;
+ internal->priority = priority;
+ internal->is_alternate = is_alternate;
- if (git_vector_insert(&odb->backends, backend) < 0)
+ if (git_vector_insert(&odb->backends, internal) < 0) {
+ free(internal);
return GIT_ENOMEM;
+ }
git_vector_sort(&odb->backends);
+ internal->backend->odb = odb;
return GIT_SUCCESS;
}
-static int add_default_backends(git_odb *db, const char *objects_dir)
+int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
+{
+ return add_backend_internal(odb, backend, priority, 0);
+}
+
+int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
+{
+ return add_backend_internal(odb, backend, priority, 1);
+}
+
+static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
{
git_odb_backend *loose, *packed;
int error;
if (error < GIT_SUCCESS)
return error;
- error = git_odb_add_backend(db, loose);
+ error = add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates);
if (error < GIT_SUCCESS)
return error;
if (error < GIT_SUCCESS)
return error;
- error = git_odb_add_backend(db, packed);
+ error = add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates);
if (error < GIT_SUCCESS)
return error;
/* add each alternate as a new backend; one alternate per line */
while ((error == GIT_SUCCESS) && (buffer = git__strtok(alternate, buffer, "\r\n")) != NULL)
- error = add_default_backends(odb, alternate);
+ error = add_default_backends(odb, alternate, 1);
gitfo_free_buf(&alternates_buf);
return error;
if ((error = git_odb_new(&db)) < 0)
return error;
- if ((error = add_default_backends(db, objects_dir)) < GIT_SUCCESS)
+ if ((error = add_default_backends(db, objects_dir, 0)) < GIT_SUCCESS)
goto cleanup;
if ((error = load_alternates(db, objects_dir)) < GIT_SUCCESS)
return;
for (i = 0; i < db->backends.length; ++i) {
- git_odb_backend *b = git_vector_get(&db->backends, i);
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *backend = internal->backend;
- if (b->free) b->free(b);
- else free(b);
+ if (backend->free) backend->free(backend);
+ else free(backend);
+
+ free(internal);
}
git_vector_free(&db->backends);
assert(db && id);
for (i = 0; i < db->backends.length && !found; ++i) {
- git_odb_backend *b = git_vector_get(&db->backends, i);
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
if (b->exists != NULL)
found = b->exists(b, id);
assert(out && db && id);
for (i = 0; i < db->backends.length && error < 0; ++i) {
- git_odb_backend *b = git_vector_get(&db->backends, i);
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
if (b->read_header != NULL)
error = b->read_header(out, b, id);
assert(out && db && id);
for (i = 0; i < db->backends.length && error < 0; ++i) {
- git_odb_backend *b = git_vector_get(&db->backends, i);
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
if (b->read != NULL)
error = b->read(out, b, id);
assert(obj && db && id);
for (i = 0; i < db->backends.length && error < 0; ++i) {
- git_odb_backend *b = git_vector_get(&db->backends, i);
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
if (b->write != NULL)
error = b->write(id, b, obj);
backend->parent.exists = &loose_backend__exists;
backend->parent.free = &loose_backend__free;
- backend->parent.priority = 2; /* higher than packfiles */
-
*backend_out = (git_odb_backend *)backend;
return GIT_SUCCESS;
}
backend->parent.exists = &pack_backend__exists;
backend->parent.free = &pack_backend__free;
- backend->parent.priority = 1;
-
*backend_out = (git_odb_backend *)backend;
return GIT_SUCCESS;
}
if (git_odb_backend_sqlite(&sqlite, ":memory") < GIT_SUCCESS)
return NULL;
- if (git_odb_add_backend(odb, sqlite) < GIT_SUCCESS)
+ if (git_odb_add_backend(odb, sqlite, 0) < GIT_SUCCESS)
return NULL;
return odb;
--- /dev/null
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "test_lib.h"
+#include "test_helpers.h"
+
+#include "odb.h"
+#include "git2/odb_backend.h"
+
+typedef struct {
+ git_odb_backend base;
+ int position;
+} fake_backend;
+
+git_odb_backend *new_backend(int position)
+{
+ fake_backend *b;
+
+ b = git__malloc(sizeof(fake_backend));
+ if (b == NULL)
+ return NULL;
+
+ memset(b, 0x0, sizeof(fake_backend));
+ b->position = position;
+ return (git_odb_backend *)b;
+}
+
+int test_backend_sorting(git_odb *odb)
+{
+ unsigned int i;
+
+ for (i = 0; i < odb->backends.length; ++i) {
+ fake_backend *internal = *((fake_backend **)git_vector_get(&odb->backends, i));
+
+ if (internal == NULL)
+ return GIT_ERROR;
+
+ if (internal->position != (int)i)
+ return GIT_ERROR;
+ }
+
+ return GIT_SUCCESS;
+}
+
+BEGIN_TEST("odb", backend_sorting)
+ git_odb *odb;
+ must_pass(git_odb_new(&odb));
+ must_pass(git_odb_add_backend(odb, new_backend(0), 5));
+ must_pass(git_odb_add_backend(odb, new_backend(2), 3));
+ must_pass(git_odb_add_backend(odb, new_backend(1), 4));
+ must_pass(git_odb_add_backend(odb, new_backend(3), 1));
+ must_pass(test_backend_sorting(odb));
+ git_odb_close(odb);
+END_TEST
+
+BEGIN_TEST("odb", backend_alternates_sorting)
+ git_odb *odb;
+ must_pass(git_odb_new(&odb));
+ must_pass(git_odb_add_backend(odb, new_backend(0), 5));
+ must_pass(git_odb_add_backend(odb, new_backend(2), 3));
+ must_pass(git_odb_add_backend(odb, new_backend(1), 4));
+ must_pass(git_odb_add_backend(odb, new_backend(3), 1));
+ must_pass(git_odb_add_alternate(odb, new_backend(4), 5));
+ must_pass(git_odb_add_alternate(odb, new_backend(6), 3));
+ must_pass(git_odb_add_alternate(odb, new_backend(5), 4));
+ must_pass(git_odb_add_alternate(odb, new_backend(7), 1));
+ must_pass(test_backend_sorting(odb));
+ git_odb_close(odb);
+END_TEST
+
+git_testsuite *libgit2_suite_repository(void)
+{
+ git_testsuite *suite = git_testsuite_new("Repository");
+
+ ADD_TEST(suite, "odb", backend_sorting);
+ ADD_TEST(suite, "odb", backend_alternates_sorting);
+
+ return suite;
+}
extern git_testsuite *libgit2_suite_tree(void);
extern git_testsuite *libgit2_suite_refs(void);
extern git_testsuite *libgit2_suite_sqlite(void);
+extern git_testsuite *libgit2_suite_repository(void);
typedef git_testsuite *(*libgit2_suite)(void);
libgit2_suite_tree,
libgit2_suite_refs,
libgit2_suite_sqlite,
+ libgit2_suite_repository,
};
#define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods))