]> git.proxmox.com Git - libgit2.git/blobdiff - src/refdb_fs.c
New upstream version 1.0.0+dfsg.1
[libgit2.git] / src / refdb_fs.c
index d582cb8a24c9a6b4711a0d330f4150ee35b4fada..1e53b3af540b3cbf9de21e350857e3b5157fca18 100644 (file)
 #include "refs.h"
 #include "hash.h"
 #include "repository.h"
-#include "fileops.h"
+#include "futils.h"
 #include "filebuf.h"
 #include "pack.h"
+#include "parse.h"
 #include "reflog.h"
 #include "refdb.h"
 #include "iterator.h"
 #include "sortedcache.h"
 #include "signature.h"
+#include "wildmatch.h"
 
 #include <git2/tag.h>
 #include <git2/object.h>
@@ -327,21 +329,33 @@ static int refdb_fs_backend__exists(
        git_refdb_backend *_backend,
        const char *ref_name)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        git_buf ref_path = GIT_BUF_INIT;
        int error;
 
        assert(backend);
 
-       if ((error = packed_reload(backend)) < 0 ||
-               (error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0)
-               return error;
+       *exists = 0;
+
+       if ((error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0)
+               goto out;
+
+       if (git_path_isfile(ref_path.ptr)) {
+               *exists = 1;
+               goto out;
+       }
+
+       if ((error = packed_reload(backend)) < 0)
+               goto out;
 
-       *exists = git_path_isfile(ref_path.ptr) ||
-               (git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
+       if (git_sortedcache_lookup(backend->refcache, ref_name) != NULL) {
+               *exists = 1;
+               goto out;
+       }
 
+out:
        git_buf_dispose(&ref_path);
-       return 0;
+       return error;
 }
 
 static const char *loose_parse_symbolic(git_buf *file_content)
@@ -457,7 +471,7 @@ static int refdb_fs_backend__lookup(
        git_refdb_backend *_backend,
        const char *ref_name)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        int error;
 
        assert(backend);
@@ -490,7 +504,7 @@ typedef struct {
 
 static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
 {
-       refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
+       refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
 
        git_vector_free(&iter->loose);
        git_pool_clear(&iter->pool);
@@ -552,7 +566,6 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
 
        while (!error && !git_iterator_advance(&entry, fsit)) {
                const char *ref_name;
-               struct packref *ref;
                char *ref_dup;
 
                git_buf_truncate(&path, ref_prefix_len);
@@ -560,15 +573,9 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
                ref_name = git_buf_cstr(&path);
 
                if (git__suffixcmp(ref_name, ".lock") == 0 ||
-                       (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
+                       (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
                        continue;
 
-               git_sortedcache_rlock(backend->refcache);
-               ref = git_sortedcache_lookup(backend->refcache, ref_name);
-               if (ref)
-                       ref->flags |= PACKREF_SHADOWED;
-               git_sortedcache_runlock(backend->refcache);
-
                ref_dup = git_pool_strdup(&iter->pool, ref_name);
                if (!ref_dup)
                        error = -1;
@@ -586,24 +593,24 @@ static int refdb_fs_backend__iterator_next(
        git_reference **out, git_reference_iterator *_iter)
 {
        int error = GIT_ITEROVER;
-       refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
-       refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
+       refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
        struct packref *ref;
 
        while (iter->loose_pos < iter->loose.length) {
                const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
 
-               if (loose_lookup(out, backend, path) == 0)
+               if (loose_lookup(out, backend, path) == 0) {
+                       ref = git_sortedcache_lookup(iter->cache, path);
+                       if (ref)
+                               ref->flags |= PACKREF_SHADOWED;
+
                        return 0;
+               }
 
                git_error_clear();
        }
 
-       if (!iter->cache) {
-               if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
-                       return error;
-       }
-
        error = GIT_ITEROVER;
        while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
                ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
@@ -612,7 +619,7 @@ static int refdb_fs_backend__iterator_next(
 
                if (ref->flags & PACKREF_SHADOWED)
                        continue;
-               if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
+               if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
                        continue;
 
                *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
@@ -627,14 +634,19 @@ static int refdb_fs_backend__iterator_next_name(
        const char **out, git_reference_iterator *_iter)
 {
        int error = GIT_ITEROVER;
-       refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
-       refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
+       refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
        struct packref *ref;
 
        while (iter->loose_pos < iter->loose.length) {
                const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
+               struct packref *ref;
 
                if (loose_lookup(NULL, backend, path) == 0) {
+                       ref = git_sortedcache_lookup(iter->cache, path);
+                       if (ref)
+                               ref->flags |= PACKREF_SHADOWED;
+
                        *out = path;
                        return 0;
                }
@@ -642,11 +654,6 @@ static int refdb_fs_backend__iterator_next_name(
                git_error_clear();
        }
 
-       if (!iter->cache) {
-               if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
-                       return error;
-       }
-
        error = GIT_ITEROVER;
        while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
                ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
@@ -655,7 +662,7 @@ static int refdb_fs_backend__iterator_next_name(
 
                if (ref->flags & PACKREF_SHADOWED)
                        continue;
-               if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
+               if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
                        continue;
 
                *out = ref->name;
@@ -669,40 +676,44 @@ static int refdb_fs_backend__iterator_next_name(
 static int refdb_fs_backend__iterator(
        git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
 {
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+       refdb_fs_iter *iter = NULL;
        int error;
-       refdb_fs_iter *iter;
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
        assert(backend);
 
-       if ((error = packed_reload(backend)) < 0)
-               return error;
-
        iter = git__calloc(1, sizeof(refdb_fs_iter));
        GIT_ERROR_CHECK_ALLOC(iter);
 
        git_pool_init(&iter->pool, 1);
 
-       if (git_vector_init(&iter->loose, 8, NULL) < 0)
-               goto fail;
+       if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0)
+               goto out;
 
        if (glob != NULL &&
-               (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL)
-               goto fail;
+           (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) {
+               error = GIT_ERROR_NOMEMORY;
+               goto out;
+       }
+
+       if ((error = iter_load_loose_paths(backend, iter)) < 0)
+               goto out;
+
+       if ((error = packed_reload(backend)) < 0)
+               goto out;
+
+       if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
+               goto out;
 
        iter->parent.next = refdb_fs_backend__iterator_next;
        iter->parent.next_name = refdb_fs_backend__iterator_next_name;
        iter->parent.free = refdb_fs_backend__iterator_free;
 
-       if (iter_load_loose_paths(backend, iter) < 0)
-               goto fail;
-
        *out = (git_reference_iterator *)iter;
-       return 0;
-
-fail:
-       refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
-       return -1;
+out:
+       if (error)
+               refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
+       return error;
 }
 
 static bool ref_is_available(
@@ -794,7 +805,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
        if (git_buf_joinpath(&ref_path, basedir, name) < 0)
                return -1;
 
-       filebuf_flags = GIT_FILEBUF_FORCE;
+       filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
        if (backend->fsync)
                filebuf_flags |= GIT_FILEBUF_FSYNC;
 
@@ -829,7 +840,7 @@ static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const
 {
        int error;
        git_filebuf *lock;
-       refdb_fs_backend *backend = (refdb_fs_backend *) _backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 
        lock = git__calloc(1, sizeof(git_filebuf));
        GIT_ERROR_CHECK_ALLOC(lock);
@@ -848,16 +859,17 @@ static int refdb_fs_backend__write_tail(
        const git_reference *ref,
        git_filebuf *file,
        int update_reflog,
-       const git_signature *who,
-       const char *message,
        const git_oid *old_id,
-       const char *old_target);
+       const char *old_target,
+       const git_signature *who,
+       const char *message);
 
 static int refdb_fs_backend__delete_tail(
        git_refdb_backend *_backend,
        git_filebuf *file,
        const char *ref_name,
-       const git_oid *old_id, const char *old_target);
+       const git_oid *old_id,
+       const char *old_target);
 
 static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
                                    const git_reference *ref, const git_signature *sig, const char *message)
@@ -868,7 +880,7 @@ static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, i
        if (success == 2)
                error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
        else if (success)
-               error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, sig, message, NULL, NULL);
+               error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message);
        else
                git_filebuf_cleanup(lock);
 
@@ -1087,6 +1099,35 @@ fail:
        return error;
 }
 
+static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
+{
+       size_t pack_pos;
+       int error, found = 0;
+
+       if ((error = packed_reload(backend)) < 0)
+               goto cleanup;
+
+       if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+               goto cleanup;
+
+       /* If a packed reference exists, remove it from the packfile and repack if necessary */
+       error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name);
+       if (error == 0) {
+               error = git_sortedcache_remove(backend->refcache, pack_pos);
+               found = 1;
+       }
+       if (error == GIT_ENOTFOUND)
+               error = 0;
+
+       git_sortedcache_wunlock(backend->refcache);
+
+       if (found)
+               error = packed_write(backend);
+
+cleanup:
+       return error;
+}
+
 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
 static int has_reflog(git_repository *repo, const char *name);
 
@@ -1094,7 +1135,7 @@ static int should_write_reflog(int *write, git_repository *repo, const char *nam
 {
        int error, logall;
 
-       error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
+       error = git_repository__configmap_lookup(&logall, repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
        if (error < 0)
                return error;
 
@@ -1239,7 +1280,7 @@ static int refdb_fs_backend__write(
        const git_oid *old_id,
        const char *old_target)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        git_filebuf file = GIT_FILEBUF_INIT;
        int error = 0;
 
@@ -1252,7 +1293,7 @@ static int refdb_fs_backend__write(
        if ((error = loose_lock(&file, backend, ref->name)) < 0)
                return error;
 
-       return refdb_fs_backend__write_tail(_backend, ref, &file, true, who, message, old_id, old_target);
+       return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
 }
 
 static int refdb_fs_backend__write_tail(
@@ -1260,12 +1301,12 @@ static int refdb_fs_backend__write_tail(
        const git_reference *ref,
        git_filebuf *file,
        int update_reflog,
-       const git_signature *who,
-       const char *message,
        const git_oid *old_id,
-       const char *old_target)
+       const char *old_target,
+       const git_signature *who,
+       const char *message)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        int error = 0, cmp = 0, should_write;
        const char *new_target = NULL;
        const git_oid *new_id = NULL;
@@ -1313,10 +1354,10 @@ on_error:
         return error;
 }
 
-static void refdb_fs_backend__try_delete_empty_ref_hierarchie(
+static void refdb_fs_backend__prune_refs(
        refdb_fs_backend *backend,
        const char *ref_name,
-       bool reflog)
+       const char *prefix)
 {
        git_buf relative_path = GIT_BUF_INIT;
        git_buf base_path = GIT_BUF_INIT;
@@ -1334,8 +1375,8 @@ static void refdb_fs_backend__try_delete_empty_ref_hierarchie(
 
                git_buf_truncate(&relative_path, commonlen);
 
-               if (reflog) {
-                       if (git_buf_join3(&base_path, '/', backend->commonpath, GIT_REFLOG_DIR, git_buf_cstr(&relative_path)) < 0)
+               if (prefix) {
+                       if (git_buf_join3(&base_path, '/', backend->commonpath, prefix, git_buf_cstr(&relative_path)) < 0)
                                goto cleanup;
                } else {
                        if (git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)) < 0)
@@ -1355,7 +1396,7 @@ static int refdb_fs_backend__delete(
        const char *ref_name,
        const git_oid *old_id, const char *old_target)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        git_filebuf file = GIT_FILEBUF_INIT;
        int error = 0;
 
@@ -1372,17 +1413,34 @@ static int refdb_fs_backend__delete(
        return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
 }
 
+static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
+{
+       git_buf loose_path = GIT_BUF_INIT;
+       int error = 0;
+
+       if (git_buf_joinpath(&loose_path, backend->commonpath, ref_name) < 0)
+               return -1;
+
+       error = p_unlink(loose_path.ptr);
+       if (error < 0 && errno == ENOENT)
+               error = GIT_ENOTFOUND;
+       else if (error != 0)
+               error = -1;
+
+       git_buf_dispose(&loose_path);
+
+       return error;
+}
+
 static int refdb_fs_backend__delete_tail(
        git_refdb_backend *_backend,
        git_filebuf *file,
        const char *ref_name,
        const git_oid *old_id, const char *old_target)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
-       git_buf loose_path = GIT_BUF_INIT;
-       size_t pack_pos;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        int error = 0, cmp = 0;
-       bool loose_deleted = 0;
+       bool packed_deleted = 0;
 
        error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
        if (error < 0)
@@ -1394,44 +1452,41 @@ static int refdb_fs_backend__delete_tail(
                goto cleanup;
        }
 
-       /* If a loose reference exists, remove it from the filesystem */
-       if (git_buf_joinpath(&loose_path, backend->commonpath, ref_name) < 0)
-               return -1;
-
-
-       error = p_unlink(loose_path.ptr);
-       if (error < 0 && errno == ENOENT)
-               error = 0;
-       else if (error < 0)
+       /*
+        * To ensure that an external observer will see either the current ref value
+        * (because the loose ref still exists), or a missing ref (after the packed-file is
+        * unlocked, there will be nothing left), we must ensure things happen in the
+        * following order:
+        *
+        * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
+        * - we optimistically delete a packed ref, keeping track of whether it existed
+        * - we delete the loose ref, note that we have its .lock
+        * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
+        * - we should prune the path components if a loose ref was deleted
+        *
+        * Note that, because our packed backend doesn't expose its filesystem lock,
+        * we might not be able to guarantee that this is what actually happens (ie.
+        * as our current code never write packed-refs.lock, nothing stops observers
+        * from grabbing a "stale" value from there).
+        */
+       if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
                goto cleanup;
-       else if (error == 0)
-               loose_deleted = 1;
 
-       if ((error = packed_reload(backend)) < 0)
-               goto cleanup;
+       if (error == 0)
+               packed_deleted = 1;
 
-       /* If a packed reference exists, remove it from the packfile and repack */
-       if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+       if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
                goto cleanup;
 
-       if (!(error = git_sortedcache_lookup_index(
-                       &pack_pos, backend->refcache, ref_name)))
-               error = git_sortedcache_remove(backend->refcache, pack_pos);
-
-       git_sortedcache_wunlock(backend->refcache);
-
        if (error == GIT_ENOTFOUND) {
-               error = loose_deleted ? 0 : ref_error_notfound(ref_name);
+               error = packed_deleted ? 0 : ref_error_notfound(ref_name);
                goto cleanup;
        }
 
-       error = packed_write(backend);
-
 cleanup:
-       git_buf_dispose(&loose_path);
        git_filebuf_cleanup(file);
-       if (loose_deleted)
-               refdb_fs_backend__try_delete_empty_ref_hierarchie(backend, ref_name, false);
+       if (error == 0)
+               refdb_fs_backend__prune_refs(backend, ref_name, "");
        return error;
 }
 
@@ -1446,8 +1501,8 @@ static int refdb_fs_backend__rename(
        const git_signature *who,
        const char *message)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
-       git_reference *old, *new;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+       git_reference *old, *new = NULL;
        git_filebuf file = GIT_FILEBUF_INIT;
        int error;
 
@@ -1463,7 +1518,7 @@ static int refdb_fs_backend__rename(
                return error;
        }
 
-       new = git_reference__set_name(old, new_name);
+       new = git_reference__realloc(&old, new_name);
        if (!new) {
                git_reference_free(old);
                return -1;
@@ -1502,7 +1557,7 @@ static int refdb_fs_backend__rename(
 static int refdb_fs_backend__compress(git_refdb_backend *_backend)
 {
        int error;
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 
        assert(backend);
 
@@ -1516,7 +1571,7 @@ static int refdb_fs_backend__compress(git_refdb_backend *_backend)
 
 static void refdb_fs_backend__free(git_refdb_backend *_backend)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 
        assert(backend);
 
@@ -1597,70 +1652,57 @@ static int reflog_alloc(git_reflog **reflog, const char *name)
 
 static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
 {
-       const char *ptr;
-       git_reflog_entry *entry;
-
-#define seek_forward(_increase) do { \
-       if (_increase >= buf_size) { \
-               git_error_set(GIT_ERROR_INVALID, "ran out of data while parsing reflog"); \
-               goto fail; \
-       } \
-       buf += _increase; \
-       buf_size -= _increase; \
-       } while (0)
-
-       while (buf_size > GIT_REFLOG_SIZE_MIN) {
-               entry = git__calloc(1, sizeof(git_reflog_entry));
-               GIT_ERROR_CHECK_ALLOC(entry);
+       git_parse_ctx parser = GIT_PARSE_CTX_INIT;
 
-               entry->committer = git__calloc(1, sizeof(git_signature));
-               GIT_ERROR_CHECK_ALLOC(entry->committer);
+       if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
+               return -1;
 
-               if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
-                       goto fail;
-               seek_forward(GIT_OID_HEXSZ + 1);
+       for (; parser.remain_len; git_parse_advance_line(&parser)) {
+               git_reflog_entry *entry;
+               const char *sig;
+               char c;
 
-               if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
-                       goto fail;
-               seek_forward(GIT_OID_HEXSZ + 1);
+               entry = git__calloc(1, sizeof(*entry));
+               GIT_ERROR_CHECK_ALLOC(entry);
+               entry->committer = git__calloc(1, sizeof(*entry->committer));
+               GIT_ERROR_CHECK_ALLOC(entry->committer);
 
-               ptr = buf;
+               if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
+                   git_parse_advance_expected(&parser, " ", 1) < 0 ||
+                   git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
+                       goto next;
 
-               /* Seek forward to the end of the signature. */
-               while (*buf && *buf != '\t' && *buf != '\n')
-                       seek_forward(1);
+               sig = parser.line;
+               while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
+                       git_parse_advance_chars(&parser, 1);
 
-               if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
-                       goto fail;
+               if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
+                       goto next;
 
-               if (*buf == '\t') {
-                       /* We got a message. Read everything till we reach LF. */
-                       seek_forward(1);
-                       ptr = buf;
+               if (c == '\t') {
+                       size_t len;
+                       git_parse_advance_chars(&parser, 1);
 
-                       while (*buf && *buf != '\n')
-                               seek_forward(1);
+                       len = parser.line_len;
+                       if (parser.line[len - 1] == '\n')
+                               len--;
 
-                       entry->msg = git__strndup(ptr, buf - ptr);
+                       entry->msg = git__strndup(parser.line, len);
                        GIT_ERROR_CHECK_ALLOC(entry->msg);
-               } else
-                       entry->msg = NULL;
+               }
 
-               while (*buf && *buf == '\n' && buf_size > 1)
-                       seek_forward(1);
+               if ((git_vector_insert(&log->entries, entry)) < 0) {
+                       git_reflog_entry__free(entry);
+                       return -1;
+               }
 
-               if (git_vector_insert(&log->entries, entry) < 0)
-                       goto fail;
+               continue;
+
+next:
+               git_reflog_entry__free(entry);
        }
 
        return 0;
-
-#undef seek_forward
-
-fail:
-       git_reflog_entry__free(entry);
-
-       return -1;
 }
 
 static int create_new_reflog_file(const char *filepath)
@@ -1694,7 +1736,7 @@ static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *
 
        assert(_backend && name);
 
-       backend = (refdb_fs_backend *) _backend;
+       backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        repo = backend->repo;
 
        if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
@@ -1727,7 +1769,7 @@ static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *nam
 
        assert(_backend && name);
 
-       backend = (refdb_fs_backend *) _backend;
+       backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 
        return has_reflog(backend->repo, name);
 }
@@ -1743,7 +1785,7 @@ static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend,
 
        assert(out && _backend && name);
 
-       backend = (refdb_fs_backend *) _backend;
+       backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        repo = backend->repo;
 
        if (reflog_alloc(&log, name) < 0)
@@ -1802,8 +1844,15 @@ static int serialize_reflog_entry(
        git_buf_rtrim(buf);
 
        if (msg) {
+               size_t i;
+
                git_buf_putc(buf, '\t');
                git_buf_puts(buf, msg);
+
+               for (i = 0; i < buf->size - 2; i++)
+                       if (buf->ptr[i] == '\n')
+                               buf->ptr[i] = ' ';
+               git_buf_rtrim(buf);
        }
 
        git_buf_putc(buf, '\n');
@@ -1853,7 +1902,7 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo
 
        assert(_backend && reflog);
 
-       backend = (refdb_fs_backend *) _backend;
+       backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 
        if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
                return -1;
@@ -1894,7 +1943,7 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
            !(old && new))
                return 0;
 
-       /* From here on is_symoblic also means that it's HEAD */
+       /* From here on is_symbolic also means that it's HEAD */
 
        if (old) {
                git_oid_cpy(&old_id, old);
@@ -1975,7 +2024,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_
 
        assert(_backend && old_name && new_name);
 
-       backend = (refdb_fs_backend *) _backend;
+       backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        repo = backend->repo;
 
        if ((error = git_reference__normalize_name(
@@ -2046,7 +2095,7 @@ cleanup:
 
 static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
 {
-       refdb_fs_backend *backend = (refdb_fs_backend *) _backend;
+       refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
        git_buf path = GIT_BUF_INIT;
        int error;
 
@@ -2061,7 +2110,7 @@ static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name
        if ((error = p_unlink(path.ptr)) < 0)
                goto out;
 
-       refdb_fs_backend__try_delete_empty_ref_hierarchie(backend, name, true);
+       refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
 
 out:
        git_buf_dispose(&path);
@@ -2080,6 +2129,9 @@ int git_refdb_backend_fs(
        backend = git__calloc(1, sizeof(refdb_fs_backend));
        GIT_ERROR_CHECK_ALLOC(backend);
 
+       if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0)
+               goto fail;
+
        backend->repo = repository;
 
        if (repository->gitdir) {
@@ -2104,15 +2156,15 @@ int git_refdb_backend_fs(
 
        git_buf_dispose(&gitpath);
 
-       if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
+       if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) {
                backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
                backend->direach_flags  |= GIT_PATH_DIR_IGNORE_CASE;
        }
-       if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
+       if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) {
                backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
                backend->direach_flags  |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
        }
-       if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) ||
+       if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) ||
                git_repository__fsync_gitdir)
                backend->fsync = 1;
        backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;