summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-05-16 11:09:15 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-05-16 11:09:15 +0200
commit973f3ee8a124fc44967a5eb59567b5993f5a92ce (patch)
treed1fe672ec6937795926153b3d7a77f2690713c45
parentec8a949a58864272860a4838c6b3d862beda7076 (diff)
downloadlibgit2-973f3ee8a124fc44967a5eb59567b5993f5a92ce.tar.gz
refdb: provide a safe way to update a reflog
Updating a reflog must be done under its ref's lock in order to stop other processes from changing the on-disk reflog or reference. Provide a function to call callback while under this lock, which will write out the modified contents upon successful return.
-rw-r--r--include/git2/refs.h16
-rw-r--r--include/git2/sys/refdb_backend.h5
-rw-r--r--src/refdb.c7
-rw-r--r--src/refdb_fs.c33
-rw-r--r--src/refs.c13
5 files changed, 73 insertions, 1 deletions
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 6a1db65a8..f4059b887 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -724,7 +724,21 @@ GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
*/
GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref);
-
+/**
+ * Safely update a reflog
+ *
+ * Changes to the reflog must be done while under the lock of its
+ * reference. This function will lock the ref, read its reflog and
+ * pass it to the callback. If the callback returns 0, the contents of
+ * the reflog object will be written to out.
+ *
+ * @param repo the repository in which the reference exists
+ * @param refname the reference's name
+ * @param cb the callback which will perform the transformation
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_update_reflog(git_repository *repo, const char *refname,
+ int (*cb)(git_reflog *reflog));
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index dce142c77..9e8ed8ac6 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -109,6 +109,11 @@ struct git_refdb_backend {
int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
/**
+ * Update the reflog via changing of the provided git_reflog
+ */
+ int (*update_reflog)(git_refdb_backend *backend, const char *refname, int (*cb)(git_reflog *reflog));
+
+ /**
* Suggests that the given refdb compress or optimize its references.
* This mechanism is implementation specific. (For on-disk reference
* databases, this may pack all loose references.) A refdb
diff --git a/src/refdb.c b/src/refdb.c
index 3e7a592f8..d7f9df600 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -207,6 +207,13 @@ int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *
return db->backend->del(db->backend, ref_name, old_id, old_target);
}
+int git_refdb_update_reflog(git_refdb *db, const char *refname, int (*cb)(git_reflog *reflog))
+{
+ assert(db && db->backend);
+
+ return db->backend->update_reflog(db->backend, refname, cb);
+}
+
int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
{
int error;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f9bd4eab5..41a22c318 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -63,6 +63,9 @@ typedef struct refdb_fs_backend {
uint32_t direach_flags;
} refdb_fs_backend;
+static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name);
+static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog);
+
static int packref_cmp(const void *a_, const void *b_)
{
const struct packref *a = a_, *b = b_;
@@ -1257,6 +1260,36 @@ static int refdb_fs_backend__rename(
return 0;
}
+static int refdb_fs_backend__update_reflog(git_refdb_backend *_backend, const char *refname,
+ int (*cb)(git_reflog *reflog))
+{
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_reflog *reflog;
+ int error;
+
+ assert(backend && refname && cb);
+
+ if ((error = loose_lock(&file, backend, refname)) < 0)
+ return error;
+
+ if ((error = refdb_reflog_fs__read(&reflog, _backend, refname)) < 0)
+ goto cleanup;
+
+ if ((error = cb(reflog)))
+ goto cleanup;
+
+ /* on successful return, write out the reflog */
+
+ error = refdb_reflog_fs__write(_backend, reflog);
+
+cleanup:
+ git_reflog_free(reflog);
+ git_filebuf_cleanup(&file);
+
+ return error;
+}
+
static int refdb_fs_backend__compress(git_refdb_backend *_backend)
{
refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
diff --git a/src/refs.c b/src/refs.c
index 9428f617d..8dd3b28b2 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1262,3 +1262,16 @@ const char *git_reference_shorthand(const git_reference *ref)
/* No shorthands are avaiable, so just return the name */
return name;
}
+
+int git_reference_update_reflog(git_repository *repo, const char *refname, int (*cb)(git_reflog *reflog))
+{
+ git_refdb *db;
+ int error;
+
+ assert(repo && refname && cb);
+
+ if ((error = git_repository_refdb__weakptr(&db, repo)) < 0)
+ return error;
+
+ return git_refdb_update_reflog(db, refname, cb);
+}