/* * 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 "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; }