diff --git a/include/git2/refs.h b/include/git2/refs.h index 2dc137692..31bf997fe 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -555,6 +555,18 @@ GIT_EXTERN(int) git_reference_foreach_glob( */ GIT_EXTERN(int) git_reference_has_log(git_reference *ref); +/** + * Ensure there is a reflog for a particular reference. + * + * Make sure that successive updates to the reference will append to + * its log. + * + * @param repo the repository + * @param refname the reference's name + * @return 0 or an error code. + */ +GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname); + /** * Check if a reference is a local branch. * diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 131e1b5d0..1485ed73a 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -116,6 +116,12 @@ struct git_refdb_backend { */ int (*compress)(git_refdb_backend *backend); + /** + * Make sure a particular reference will have a reflog which + * will be appended to on writes. + */ + int (*ensure_log)(git_refdb_backend *backend, const char *refname); + /** * Frees any resources held by the refdb. A refdb implementation may * provide this function; if it is not provided, nothing will be done. diff --git a/src/refdb.c b/src/refdb.c index bc8c2b366..4f3169f88 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -221,3 +221,10 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name) return 0; } + +int git_refdb_ensure_log(git_refdb *db, const char *refname) +{ + assert(db && refname); + + return db->backend->ensure_log(db->backend, refname); +} diff --git a/src/refdb.h b/src/refdb.h index 215ae17c5..12c15cb9f 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -48,5 +48,8 @@ int git_refdb_delete(git_refdb *refdb, const char *ref_name); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); int git_refdb_reflog_write(git_reflog *reflog); +int git_refdb_ensure_log(git_refdb *refdb, const char *refname); + + #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 9f23de248..2cdea8274 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1233,7 +1233,7 @@ static int create_new_reflog_file(const char *filepath) return error; if ((fd = p_open(filepath, - O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT, GIT_REFLOG_FILE_MODE)) < 0) return -1; @@ -1245,6 +1245,24 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name); } +static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) +{ + refdb_fs_backend *backend; + git_repository *repo; + git_buf path = GIT_BUF_INIT; + int error; + + assert(_backend && name); + + backend = (refdb_fs_backend *) _backend; + repo = backend->repo; + + if ((error = retrieve_reflog_path(&path, repo, name)) < 0) + return error; + + return create_new_reflog_file(git_buf_cstr(&path)); +} + static int has_reflog(git_repository *repo, const char *name) { int ret = 0; @@ -1590,6 +1608,7 @@ int git_refdb_backend_fs( backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; + backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; backend->parent.reflog_read = &refdb_reflog_fs__read; backend->parent.reflog_write = &refdb_reflog_fs__write; diff --git a/src/refs.c b/src/refs.c index 598d6873c..902a17cfc 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1068,6 +1068,19 @@ int git_reference_has_log( return result; } +int git_reference_ensure_log(git_repository *repo, const char *refname) +{ + int error; + git_refdb *refdb; + + assert(repo && refname); + + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + return error; + + return git_refdb_ensure_log(refdb, refname); +} + int git_reference__is_branch(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; diff --git a/src/stash.c b/src/stash.c index 66b1cd7c5..7d7bf78c3 100644 --- a/src/stash.c +++ b/src/stash.c @@ -414,6 +414,9 @@ static int update_reflog( git_reference *stash; int error; + if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0) + return error; + error = git_reference_create_with_log(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, stasher, message); git_reference_free(stash); diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 6c9375dc7..9f414f21d 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -212,12 +212,9 @@ void test_refs_reflog_reflog__write_when_explicitly_active(void) { git_reference *ref; git_oid id; - git_buf path = GIT_BUF_INIT; git_oid_fromstr(&id, current_master_tip); - cl_git_pass(git_buf_join(&path, '/', git_repository_path(g_repo), "logs/refs/tags/foo")); - cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&path), 0777)); - cl_git_mkfile(git_buf_cstr(&path), ""); + git_reference_ensure_log(g_repo, "refs/tags/foo"); cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1)); git_reference_free(ref);