diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-05-16 11:09:15 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-05-16 11:09:15 +0200 |
commit | 973f3ee8a124fc44967a5eb59567b5993f5a92ce (patch) | |
tree | d1fe672ec6937795926153b3d7a77f2690713c45 | |
parent | ec8a949a58864272860a4838c6b3d862beda7076 (diff) | |
download | libgit2-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.h | 16 | ||||
-rw-r--r-- | include/git2/sys/refdb_backend.h | 5 | ||||
-rw-r--r-- | src/refdb.c | 7 | ||||
-rw-r--r-- | src/refdb_fs.c | 33 | ||||
-rw-r--r-- | src/refs.c | 13 |
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); +} |