Commit 8d5ec9106afbca346a8def84ef20de0a7ba2240a

Carlos Martín Nieto 2013-11-23T14:13:01

refs: expose a way to ensure a ref has a log Sometimes (e.g. stash) we want to make sure that a log will be written, even if it's not in one of the standard locations. Let's make that easier.

diff --git a/include/git2/refs.h b/include/git2/refs.h
index 2dc1376..31bf997 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -556,6 +556,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.
  *
  * @param ref A git reference
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index 131e1b5..1485ed7 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -117,6 +117,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 bc8c2b3..4f3169f 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 215ae17..12c15cb 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 9f23de2..2cdea82 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 598d687..902a17c 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 66b1cd7..7d7bf78 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 6c9375d..9f414f2 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);