From 5f6f7245e82ec420f4dda53285c3c2fdc4efc4b9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Mar 2020 19:35:06 +0100 Subject: refdb reftable backend --- src/refdb_reftable.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/refdb_reftable.h | 8 ++ 2 files changed, 393 insertions(+) create mode 100644 src/refdb_reftable.c create mode 100644 src/refdb_reftable.h 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); -- cgit v1.2.1