summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2020-03-24 19:35:06 +0100
committerPatrick Steinhardt <ps@pks.im>2020-03-24 19:35:06 +0100
commit5f6f7245e82ec420f4dda53285c3c2fdc4efc4b9 (patch)
treeb4afbb5239dedeedf20c4ac5652326ee91a199de
parent8658cb03dbd98a78ca9adeddf7566cc0dd6c7224 (diff)
downloadlibgit2-5f6f7245e82ec420f4dda53285c3c2fdc4efc4b9.tar.gz
refdb reftable backend
-rw-r--r--src/refdb_reftable.c385
-rw-r--r--src/refdb_reftable.h8
2 files changed, 393 insertions, 0 deletions
diff --git a/src/refdb_reftable.c b/src/refdb_reftable.c
new file mode 100644
index 000000000..601f3bb9e
--- /dev/null
+++ b/src/refdb_reftable.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refdb_fs.h"
+
+#include "git2/sys/refdb_backend.h"
+#include "git2/sys/refs.h"
+#include "wildmatch.h"
+#include "../reftable/reftable.h"
+
+typedef struct {
+ git_refdb_backend parent;
+ git_repository *repo;
+ struct stack *stack;
+} reftable_backend;
+
+typedef struct {
+ git_reference_iterator parent;
+ struct merged_table *table;
+ struct iterator iter;
+ const char *glob;
+} reftable_iterator;
+
+static int reftable_reference_from_record(git_reference **out, struct ref_record *record)
+{
+ git_reference *ref;
+ git_oid oid, peeled;
+ int error;
+
+ if (record->target) {
+ ref = git_reference__alloc_symbolic(record->ref_name, record->target);
+ } else {
+ if ((error = git_oid_fromraw(&oid, record->value)) < 0 ||
+ (error = git_oid_fromraw(&oid, record->target_value)) < 0)
+ goto out;
+ ref = git_reference__alloc(record->ref_name, &oid, &peeled);
+ }
+ GIT_ERROR_CHECK_ALLOC(ref);
+
+ *out = ref;
+out:
+ return error;
+}
+
+int reftable_writer(struct writer *wr, void *arg)
+{
+ struct ref_record *ref = arg;
+ writer_set_limits(wr, ref->update_index, ref->update_index);
+ return writer_add_ref(wr, ref);
+}
+
+static int reftable_write_record(git_refdb_backend *_backend, struct ref_record ref)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ if (stack_add(backend->stack, reftable_writer, &ref) < 0)
+ return -1;
+ return 0;
+}
+
+static int reftable_exists(
+ int *exists,
+ git_refdb_backend *_backend,
+ const char *ref_name)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ struct ref_record ref = { 0 };
+ int error;
+
+ if ((error = stack_read_ref(backend->stack, ref_name, &ref)) < 0)
+ return error;
+ *exists = (error == 0);
+
+ ref_record_clear(&ref);
+ return 0;
+}
+
+static int reftable_lookup(
+ git_reference **out,
+ git_refdb_backend *_backend,
+ const char *ref_name)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ struct ref_record ref;
+ int error;
+
+ if ((error = stack_read_ref(backend->stack, ref_name, &ref)) < 0)
+ return error;
+ if (error > 0)
+ return GIT_ENOTFOUND;
+
+ return reftable_reference_from_record(out, &ref);
+}
+
+static int reftable_iterator_next(git_reference **out, git_reference_iterator *_it)
+{
+ reftable_iterator *it = GIT_CONTAINER_OF(_it, reftable_iterator, parent);
+ struct ref_record ref;
+ int error;
+
+ while (1) {
+ if ((error = iterator_next_ref(it->iter, &ref)) < 0)
+ return error;
+ if (error > 0)
+ return GIT_ITEROVER;
+ if (it->glob && wildmatch(it->glob, ref.ref_name, 0) != 0)
+ continue;
+ return reftable_reference_from_record(out, &ref);
+ }
+
+ return GIT_ITEROVER;
+}
+
+static int reftable_iterator_next_name(const char **out, git_reference_iterator *_it)
+{
+ reftable_iterator *it = GIT_CONTAINER_OF(_it, reftable_iterator, parent);
+ struct ref_record ref;
+ int error;
+
+ while (1) {
+ if ((error = iterator_next_ref(it->iter, &ref)) < 0)
+ return error;
+ if (error > 0)
+ return GIT_ITEROVER;
+ if (it->glob && wildmatch(it->glob, ref.ref_name, 0) != 0)
+ continue;
+
+ *out = ref.ref_name;
+ return 0;
+ }
+
+ return GIT_ITEROVER;
+}
+
+static void reftable_iterator_free(git_reference_iterator *_it)
+{
+ reftable_iterator *it = GIT_CONTAINER_OF(_it, reftable_iterator, parent);
+ merged_table_free(it->table);
+ iterator_destroy(&it->iter);
+ git__free(it);
+}
+
+static int reftable_iterator_new(
+ git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ struct merged_table *table = NULL;
+ reftable_iterator *it = NULL;
+ int error;
+
+ table = stack_merged_table(backend->stack);
+ GIT_ERROR_CHECK_ALLOC(table);
+
+ it = git__calloc(1, sizeof(*it));
+ GIT_ERROR_CHECK_ALLOC(it);
+ it->parent.next = reftable_iterator_next;
+ it->parent.next_name = reftable_iterator_next_name;
+ it->parent.free = reftable_iterator_free;
+ it->table = table;
+ it->glob = git__strdup(glob);
+ GIT_ERROR_CHECK_ALLOC(it->glob);
+
+ if ((error = merged_table_seek_ref(table, &it->iter, "")) < 0)
+ goto out;
+
+ *out = &it->parent;
+out:
+ if (error < 0) {
+ merged_table_free(table);
+ git__free(it);
+ }
+ return 0;
+}
+
+static int reftable_write(
+ git_refdb_backend *backend,
+ const git_reference *ref,
+ int force,
+ const git_signature *who,
+ const char *message,
+ const git_oid *old_id,
+ const char *old_target)
+{
+ struct ref_record record = { 0 };
+ int error;
+
+ GIT_UNUSED(force);
+ GIT_UNUSED(who);
+ GIT_UNUSED(message);
+ GIT_UNUSED(old_id);
+ GIT_UNUSED(old_target);
+
+ record.ref_name = (char *)git_reference_name(ref);
+ record.update_index = 0;
+ switch (git_reference_type(ref)) {
+ case GIT_REFERENCE_SYMBOLIC:
+ record.target = (char *) git_reference_symbolic_target(ref);
+ break;
+ case GIT_REFERENCE_DIRECT:
+ record.value = (unsigned char *) git_reference_target(ref)->id;
+ /* TODO: peel to commit */
+ record.target_value = (unsigned char *) git_reference_target_peel(ref)->id;
+ break;
+ default:
+ return -1;
+ }
+
+ if ((error = reftable_write_record(backend, record)) < 0)
+ goto out;
+
+out:
+ return error;
+}
+
+static int reftable_delete(
+ git_refdb_backend *backend,
+ const char *ref_name,
+ const git_oid *old_id,
+ const char *old_target)
+{
+ struct ref_record record = { 0 };
+ int error;
+
+ GIT_UNUSED(old_id);
+ GIT_UNUSED(old_target);
+
+ record.ref_name = (char *)ref_name;
+ record.update_index = 0;
+ record.value = NULL;
+ record.target_value = NULL;
+ record.target = NULL;
+
+ if ((error = reftable_write_record(backend, record)) < 0)
+ goto out;
+
+out:
+ return error;
+}
+
+static int reftable_rename(
+ git_reference **out,
+ git_refdb_backend *_backend,
+ const char *old_name,
+ const char *new_name,
+ int force,
+ const git_signature *who,
+ const char *message)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(old_name);
+ GIT_UNUSED(new_name);
+ GIT_UNUSED(force);
+ GIT_UNUSED(who);
+ GIT_UNUSED(message);
+ return 0;
+}
+
+static void reftable_free(git_refdb_backend *_backend)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ stack_destroy(backend->stack);
+ git__free(backend);
+}
+
+static int reftable_reflog__has_log(git_refdb_backend *backend, const char *refname)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(refname);
+ return 0;
+}
+
+static int reftable_reflog__ensure_log(git_refdb_backend *_backend, const char *name)
+{
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(name);
+ return 0;
+}
+
+static int reftable_reflog__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(name);
+ return 0;
+}
+
+static int reftable_reflog__write(git_refdb_backend *_backend, git_reflog *reflog)
+{
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(reflog);
+ return 0;
+}
+
+static int reftable_reflog__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
+{
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(old_name);
+ GIT_UNUSED(new_name);
+ return 0;
+}
+
+static int reftable_reflog__delete(git_refdb_backend *_backend, const char *name)
+{
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(name);
+ return 0;
+}
+
+static int reftable_compress(git_refdb_backend *_backend)
+{
+ reftable_backend *backend = GIT_CONTAINER_OF(_backend, reftable_backend, parent);
+ return stack_compact_all(backend->stack, NULL);
+}
+
+static int reftable_lock(void **out, git_refdb_backend *_backend, const char *refname)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(_backend);
+ GIT_UNUSED(refname);
+ return 0;
+}
+
+static int reftable_unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
+ const git_reference *ref, const git_signature *sig, const char *message)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(payload);
+ GIT_UNUSED(success);
+ GIT_UNUSED(update_reflog);
+ GIT_UNUSED(ref);
+ GIT_UNUSED(sig);
+ GIT_UNUSED(message);
+ return 0;
+}
+
+int git_refdb_backend_reftable(
+ git_refdb_backend **out,
+ git_repository *repository)
+{
+ git_buf dir = GIT_BUF_INIT, list = GIT_BUF_INIT;
+ struct write_options config = {0};
+ reftable_backend *backend;
+ struct stack *stack;
+ int error;
+
+ backend = git__calloc(1, sizeof(reftable_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ if ((error = git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION)) < 0)
+ goto out;
+ backend->repo = repository;
+
+ if ((error = git_buf_joinpath(&dir, git_repository_path(backend->repo), "reftables")) < 0 ||
+ (error = git_buf_joinpath(&list, git_repository_path(backend->repo), "refs")) < 0 ||
+ (error = new_stack(&stack, dir.ptr, list.ptr, config)) < 0)
+ goto out;
+ backend->stack = stack;
+
+ backend->parent.exists = reftable_exists;
+ backend->parent.lookup = reftable_lookup;
+ backend->parent.iterator = reftable_iterator_new;
+ backend->parent.write = reftable_write;
+ backend->parent.rename = reftable_rename;
+ backend->parent.del = reftable_delete;
+ backend->parent.has_log = reftable_reflog__has_log;
+ backend->parent.ensure_log = reftable_reflog__ensure_log;
+ backend->parent.free = reftable_free;
+ backend->parent.reflog_read = reftable_reflog__read;
+ backend->parent.reflog_write = reftable_reflog__write;
+ backend->parent.reflog_rename = reftable_reflog__rename;
+ backend->parent.reflog_delete = reftable_reflog__delete;
+ backend->parent.compress = reftable_compress;
+ backend->parent.lock = reftable_lock;
+ backend->parent.unlock = reftable_unlock;
+
+ *out = (git_refdb_backend *)backend;
+out:
+ git_buf_dispose(&dir);
+ git_buf_dispose(&list);
+ return error;
+}
diff --git a/src/refdb_reftable.h b/src/refdb_reftable.h
new file mode 100644
index 000000000..aa5b95ff1
--- /dev/null
+++ b/src/refdb_reftable.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+int git_refdb_backend_reftable(git_refdb_backend **backend_out, git_repository *repository);