Commit 7ee8c7e6776a3c5b3a45cfd10ccf205eebb3f3fe

Carlos Martín Nieto 2014-02-05T11:07:34

refs: placeholder conditional delete We don't actually pass the old value yet.

diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index 13ce9a0..aa5ef9e 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -106,7 +106,7 @@ struct git_refdb_backend {
 	 * Deletes the given reference from the refdb.  A refdb implementation
 	 * must provide this function.
 	 */
-	int (*del)(git_refdb_backend *backend, const char *ref_name);
+	int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
 
 	/**
 	 * Suggests that the given refdb compress or optimize its references.
diff --git a/src/refdb.c b/src/refdb.c
index 66d943e..984c3c7 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -201,10 +201,10 @@ int git_refdb_rename(
 	return 0;
 }
 
-int git_refdb_delete(struct git_refdb *db, const char *ref_name)
+int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
 {
 	assert(db && db->backend);
-	return db->backend->del(db->backend, ref_name);
+	return db->backend->del(db->backend, ref_name, old_id, old_target);
 }
 
 int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name)
diff --git a/src/refdb.h b/src/refdb.h
index eabb596..cbad86f 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -43,7 +43,7 @@ int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
 void git_refdb_iterator_free(git_reference_iterator *iter);
 
 int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target);
-int git_refdb_delete(git_refdb *refdb, const char *ref_name);
+int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_id, const char *old_target);
 
 int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name);
 int git_refdb_reflog_write(git_reflog *reflog);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index d1b20f8..c9f9c0f 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -688,20 +688,20 @@ static int reference_path_available(
 	return 0;
 }
 
-static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const git_reference *ref)
+static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
 {
         int error;
 	git_buf ref_path = GIT_BUF_INIT;
 
-	assert(file && backend && ref);
+	assert(file && backend && name);
 
 	/* Remove a possibly existing empty directory hierarchy
 	 * which name would collide with the reference name
 	 */
-	if (git_futils_rmdir_r(ref->name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0)
+	if (git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0)
 		return -1;
 
-	if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
+	if (git_buf_joinpath(&ref_path, backend->path, name) < 0)
 		return -1;
 
 	error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE);
@@ -930,7 +930,7 @@ static bool should_write_reflog(git_repository *repo, const char *name)
 	return 0;
 }
 
-static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const git_reference *ref,
+static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
 	const git_oid *old_id, const char *old_target)
 {
 	int error = 0;
@@ -938,7 +938,7 @@ static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const git_reference
 
 	*cmp = 0;
 	if (old_id || old_target) {
-		if ((error = refdb_fs_backend__lookup(&old_ref, backend, ref->name)) < 0)
+		if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
 			goto out;
 	}
 
@@ -965,7 +965,6 @@ static int refdb_fs_backend__write(
 {
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_filebuf file = GIT_FILEBUF_INIT;
-	git_reference *old_ref = NULL;
 	int error = 0, cmp = 0;
 
 	assert(backend);
@@ -975,10 +974,10 @@ static int refdb_fs_backend__write(
 		return error;
 
 	/* We need to perform the reflog append and old value check under the ref's lock */
-	if ((error = loose_lock(&file, backend, ref)) < 0)
+	if ((error = loose_lock(&file, backend, ref->name)) < 0)
 		return error;
 
-	if ((error = cmp_old_ref(&cmp, _backend, ref, old_id, old_target)) < 0)
+	if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
 		goto on_error;
 
 	if (cmp) {
@@ -1002,16 +1001,32 @@ on_error:
 
 static int refdb_fs_backend__delete(
 	git_refdb_backend *_backend,
-	const char *ref_name)
+	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;
-	int error = 0;
+	size_t pack_pos
+;	git_filebuf file = GIT_FILEBUF_INIT;
+	int error = 0, cmp = 0;
 	bool loose_deleted = 0;
 
 	assert(backend && ref_name);
 
+	if ((error = loose_lock(&file, backend, ref_name)) < 0)
+		return error;
+
+	error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
+	//git_filebuf_cleanup(&file);
+	if (error < 0)
+		goto cleanup;
+
+	if (cmp) {
+		giterr_set(GITERR_REFERENCE, "old reference value does not match");
+		error = GIT_EMODIFIED;
+		goto cleanup;
+	}
+
 	/* If a loose reference exists, remove it from the filesystem */
 	if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
 		return -1;
@@ -1024,14 +1039,14 @@ static int refdb_fs_backend__delete(
 	git_buf_free(&loose_path);
 
 	if (error != 0)
-		return error;
+		goto cleanup;
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		goto cleanup;
 
 	/* If a packed reference exists, remove it from the packfile and repack */
-	if (git_sortedcache_wlock(backend->refcache) < 0)
-		return -1;
+	if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+		goto cleanup;
 
 	if (!(error = git_sortedcache_lookup_index(
 			&pack_pos, backend->refcache, ref_name)))
@@ -1039,10 +1054,17 @@ static int refdb_fs_backend__delete(
 
 	git_sortedcache_wunlock(backend->refcache);
 
-	if (error == GIT_ENOTFOUND)
-		return loose_deleted ? 0 : ref_error_notfound(ref_name);
+	if (error == GIT_ENOTFOUND) {
+		error = loose_deleted ? 0 : ref_error_notfound(ref_name);
+		goto cleanup;
+	}
+
+	error = packed_write(backend);
 
-	return packed_write(backend);
+cleanup:
+	git_filebuf_cleanup(&file);
+
+	return error;
 }
 
 static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
@@ -1068,7 +1090,7 @@ static int refdb_fs_backend__rename(
 		(error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
 		return error;
 
-	if ((error = refdb_fs_backend__delete(_backend, old_name)) < 0) {
+	if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
 		git_reference_free(old);
 		return error;
 	}
@@ -1079,7 +1101,7 @@ static int refdb_fs_backend__rename(
 		return -1;
 	}
 
-	if ((error = loose_lock(&file, backend, new)) < 0) {
+	if ((error = loose_lock(&file, backend, new->name)) < 0) {
 		git_reference_free(new);
 		return error;
 	}
diff --git a/src/refs.c b/src/refs.c
index 864bfce..f45c642 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -116,7 +116,7 @@ void git_reference_free(git_reference *reference)
 
 int git_reference_delete(git_reference *ref)
 {
-	return git_refdb_delete(ref->db, ref->name);
+	return git_refdb_delete(ref->db, ref->name, NULL, NULL);
 }
 
 int git_reference_lookup(git_reference **ref_out,