Commit 911236619b5d774e33dd9f3de92a7c86c2befb26

Carlos Martín Nieto 2014-02-04T22:04:00

refdb: add conditional symbolic updates Add a parameter to the backend to allow checking for the old symbolic target.

diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index e43f996..13ce9a0 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -95,7 +95,7 @@ struct git_refdb_backend {
 	int (*write)(git_refdb_backend *backend,
 		     const git_reference *ref, int force,
 		     const git_signature *who, const char *message,
-		     const git_oid *old);
+		     const git_oid *old, const char *old_target);
 
 	int (*rename)(
 		git_reference **out, git_refdb_backend *backend,
diff --git a/src/refdb.c b/src/refdb.c
index 9ff8124..66d943e 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -167,14 +167,14 @@ void git_refdb_iterator_free(git_reference_iterator *iter)
 	iter->free(iter);
 }
 
-int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old)
+int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target)
 {
 	assert(db && db->backend);
 
 	GIT_REFCOUNT_INC(db);
 	ref->db = db;
 
-	return db->backend->write(db->backend, ref, force, who, message, old);
+	return db->backend->write(db->backend, ref, force, who, message, old_id, old_target);
 }
 
 int git_refdb_rename(
diff --git a/src/refdb.h b/src/refdb.h
index 86a1f89..eabb596 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -42,7 +42,7 @@ int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter);
 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);
+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_reflog_read(git_reflog **out, git_refdb *db,  const char *name);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 554fe42..879e485 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -936,12 +936,13 @@ static int refdb_fs_backend__write(
 	int force,
 	const git_signature *who,
 	const char *message,
-	const git_oid *old_id)
+	const git_oid *old_id,
+	const char *old_target)
 {
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_filebuf file = GIT_FILEBUF_INIT;
-	git_reference *old_ref;
-	int error = 0, cmp;
+	git_reference *old_ref = NULL;
+	int error = 0, cmp = 0;
 
 	assert(backend);
 
@@ -953,25 +954,25 @@ static int refdb_fs_backend__write(
 	if ((error = loose_lock(&file, backend, ref)) < 0)
 		return error;
 
-	if (old_id) {
+	if (old_id || old_target) {
 		if ((error = refdb_fs_backend__lookup(&old_ref, _backend, ref->name)) < 0)
 			goto on_error;
+	}
 
-		if (old_ref->type == GIT_REF_SYMBOLIC) {
-			git_reference_free(old_ref);
-			giterr_set(GITERR_REFERENCE, "cannot compare id to symbolic reference target");
-			error = -1;
-			goto on_error;
-		}
-
-		/* Finally we can compare the ids */
+	if (old_id && old_ref->type == GIT_REF_OID) {
 		cmp = git_oid_cmp(old_id, &old_ref->target.oid);
 		git_reference_free(old_ref);
-		if (cmp) {
-			giterr_set(GITERR_REFERENCE, "old reference value does not match");
-			error = GIT_EMODIFIED;
-			goto on_error;
-		}
+	}
+
+	if (old_target && old_ref->type == GIT_REF_SYMBOLIC) {
+		cmp = git__strcmp(old_target, old_ref->target.symbolic);
+		git_reference_free(old_ref);
+	}
+
+	if (cmp) {
+		giterr_set(GITERR_REFERENCE, "old reference value does not match");
+		error = GIT_EMODIFIED;
+		goto on_error;
 	}
 
 	if (should_write_reflog(backend->repo, ref->name) &&
diff --git a/src/refs.c b/src/refs.c
index 007fdf3..888b5cb 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -332,7 +332,8 @@ static int reference__create(
 	int force,
 	const git_signature *signature,
 	const char *log_message,
-	const git_oid *old_id)
+	const git_oid *old_id,
+	const char *old_target)
 {
 	char normalized[GIT_REFNAME_MAX];
 	git_refdb *refdb;
@@ -381,7 +382,7 @@ static int reference__create(
 
 	GITERR_CHECK_ALLOC(ref);
 
-	if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id)) < 0) {
+	if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) {
 		git_reference_free(ref);
 		return error;
 	}
@@ -431,7 +432,7 @@ int git_reference_create_matching(
 	}
 
 	error = reference__create(
-		ref_out, repo, name, id, NULL, force, signature, log_message, old_id);
+		ref_out, repo, name, id, NULL, force, signature, log_message, old_id, NULL);
 
 	git_signature_free(who);
 	return error;
@@ -471,7 +472,7 @@ int git_reference_symbolic_create(
 	}
 
 	error = reference__create(
-		ref_out, repo, name, NULL, target, force, signature, log_message, NULL);
+		ref_out, repo, name, NULL, target, force, signature, log_message, NULL, NULL);
 
 	git_signature_free(who);
 	return error;