Commit 32054c24a26ab386fb56c7333fa723459e7e8dff

Vicent Marti 2011-02-18T18:09:19

Brush up the refs API Changed some more API details and updated documentation. Sketched API for addition/removal of entries. Signed-off-by: Vicent Marti <tanoku@gmail.com>

diff --git a/src/git2/refs.h b/src/git2/refs.h
index 57d3985..ae77f56 100644
--- a/src/git2/refs.h
+++ b/src/git2/refs.h
@@ -44,6 +44,9 @@ GIT_BEGIN_DECL
  * The reference will be created in the repository and written
  * to the disk.
  *
+ * This reference is owned by the repository and shall not
+ * be free'd by the user.
+ *
  * @param ref_out Pointer to the newly created reference
  * @param repo Repository where that reference will live
  * @param name The name of the reference
@@ -58,6 +61,9 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos
  * The reference will be created in the repository and written
  * to the disk.
  *
+ * This reference is owned by the repository and shall not
+ * be free'd by the user.
+ *
  * @param ref_out Pointer to the newly created reference
  * @param repo Repository where that reference will live
  * @param name The name of the reference
@@ -120,22 +126,6 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
 GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref);
 
 /**
- * Write a reference back to disk.
- *
- * The reference must have a valid name and a valid target
- * (either direct or symbolic).
- *
- * If the reference has been loaded from disk and no changes
- * have been made, no action will take place.
- *
- * The writing to disk is atomic.
- *
- * @param ref The reference
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_reference_write(git_reference *ref);
-
-/**
  * Get the repository where a reference resides
  *
  * @param ref The reference
@@ -144,14 +134,13 @@ GIT_EXTERN(int) git_reference_write(git_reference *ref);
 GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref);
 
 /**
- * Set the target reference of a reference.
+ * Set the symbolic target of a reference.
  *
- * This converts the reference into a symbolic
- * reference.
+ * The reference must be a symbolic reference, otherwise
+ * this method will fail.
  *
- * This marks the reference as modified; changes
- * won't take effect until it is manually written back
- * to disk.
+ * The reference will be automatically updated in
+ * memory and on disk.
  *
  * @param ref The reference
  * @param target The new target for the reference
@@ -162,12 +151,11 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target)
 /**
  * Set the OID target of a reference.
  *
- * This converts the reference into a direct
- * reference.
+ * The reference must be a direct reference, otherwise
+ * this method will fail.
  *
- * This marks the reference as modified; changes
- * won't take effect until it is manually written back
- * to disk.
+ * The reference will be automatically updated in
+ * memory and on disk.
  *
  * @param ref The reference
  * @param target The new target OID for the reference
@@ -175,6 +163,30 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target)
  */
 GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
 
+/**
+ * Rename an existing reference
+ *
+ * This method works for both direct and symbolic references.
+ * The new name will be checked for validity and may be
+ * modified into a normalized form.
+ *
+ * The refernece will be immediately renamed in-memory
+ * and on disk.
+ *
+ */
+GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name);
+
+/**
+ * Delete an existing reference
+ *
+ * This method works for both direct and symbolic references.
+ *
+ * The reference will be immediately removed on disk and from
+ * memory. The given reference pointer will no longer be valid.
+ *
+ */
+GIT_EXTERN(int) git_reference_delete(git_reference *ref);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/refs.c b/src/refs.c
index 0cae6ee..8fa157c 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -30,6 +30,8 @@
 
 #define MAX_NESTING_LEVEL 5
 
+static int reference_write(git_reference *ref);
+
 static const int default_table_size = 32;
 
 static uint32_t reftable_hash(const void *key, int hash_id)
@@ -43,17 +45,6 @@ static uint32_t reftable_hash(const void *key, int hash_id)
 	return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
 }
 
-static int check_refname(const char *name) 
-{
-	/*
-	 * TODO: To be implemented
-	 * Check if the given name is a valid name
-	 * for a reference
-	 */
-	
-	return name ? GIT_SUCCESS : GIT_ERROR;
-}
-
 static void reference_free(git_reference *reference)
 {
 	if (reference == NULL)
@@ -68,7 +59,7 @@ static void reference_free(git_reference *reference)
 	free(reference);
 }
 
-static int reference__create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type) {
+static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type) {
 	char normalized[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH];
 	int error = GIT_SUCCESS;
 	git_reference *reference = NULL;
@@ -111,7 +102,7 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo,
 	int error = GIT_SUCCESS;
 	git_reference *ref = NULL;
 
-	error = reference__create(&ref, repo, name, GIT_REF_SYMBOLIC);
+	error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
@@ -120,14 +111,11 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo,
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
+	/* set the target; this will write the reference on disk */
 	error = git_reference_set_target(ref, normalized);
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
-	error = git_reference_write(ref);
-	if (error < GIT_SUCCESS)
-		goto cleanup;
-
 	*ref_out = ref;
 
 	return error;
@@ -142,18 +130,15 @@ int git_reference_create_oid(git_reference **ref_out, git_repository *repo, cons
 	int error = GIT_SUCCESS;
 	git_reference *ref = NULL;
 
-	error = reference__create(&ref, repo, name, GIT_REF_OID);
+	error = reference_create(&ref, repo, name, GIT_REF_OID);
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
+	/* set the oid; this will write the reference on disk */
 	error = git_reference_set_oid(ref, id);
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
-	error = git_reference_write(ref);
-	if (error < GIT_SUCCESS)
-		goto cleanup;
-
 	*ref_out = ref;
 
 	return error;
@@ -168,7 +153,6 @@ static int parse_sym_ref(git_reference *ref, gitfo_buf *file_content)
 	const unsigned int header_len = strlen(GIT_SYMREF);
 	const char *refname_start;
 	char *eol;
-	int error;
 
 	refname_start = (const char *)file_content->data;
 
@@ -182,9 +166,9 @@ static int parse_sym_ref(git_reference *ref, gitfo_buf *file_content)
 
 	refname_start += header_len;
 
-	error = git_reference_set_target(ref, refname_start);
-	if (error < GIT_SUCCESS)
-		return error;
+	ref->target.ref = git__strdup(refname_start);
+	if (ref->target.ref == NULL)
+		return GIT_ENOMEM;
 
 	/* remove newline at the end of file */
 	eol = strchr(ref->target.ref, '\n');
@@ -202,8 +186,6 @@ static int parse_oid_ref(git_reference *ref, gitfo_buf *file_content)
 {
 	char *buffer;
 	git_oid id;
-	int error;
-
 	buffer = (char *)file_content->data;
 
 	/* File format: 40 chars (OID) + newline */
@@ -213,9 +195,7 @@ static int parse_oid_ref(git_reference *ref, gitfo_buf *file_content)
 	if (git_oid_mkstr(&id, buffer) < GIT_SUCCESS)
 		return GIT_EREFCORRUPTED;
 
-	error = git_reference_set_oid(ref, &id);
-	if (error < GIT_SUCCESS)
-		return error;
+	git_oid_cpy(&ref->target.oid, &id);
 
 	buffer = buffer + GIT_OID_HEXSZ;
 	if (*buffer == '\r')
@@ -265,21 +245,19 @@ static int lookup_loose_ref(
 		goto cleanup;
 
 	if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
-		error = reference__create(&ref, repo, name, GIT_REF_SYMBOLIC);
+		error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
 		if (error < GIT_SUCCESS)
 			goto cleanup;
 
 		error = parse_sym_ref(ref, &ref_file);
 	} else {
-		error = reference__create(&ref, repo, name, GIT_REF_OID);
+		error = reference_create(&ref, repo, name, GIT_REF_OID);
 		if (error < GIT_SUCCESS)
 			goto cleanup;
 
 		error = parse_oid_ref(ref, &ref_file);
 	}
 
-	ref->modified = 0;
-
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
@@ -397,16 +375,13 @@ static int parse_packed_line(
 	if (refname[refname_len - 1] == '\r')
 		refname[refname_len - 1] = 0;
 
-	error = reference__create(&ref, repo, refname, GIT_REF_OID);
+	error = reference_create(&ref, repo, refname, GIT_REF_OID);
 	if (error < GIT_SUCCESS)
 		goto cleanup;
 
-	error = git_reference_set_oid(ref, &id);
-	if (error < GIT_SUCCESS)
-		goto cleanup;
+	git_oid_cpy(&ref->target.oid, &id);
 
 	ref->packed = 1;
-	ref->modified = 0;
 
 	*ref_out = ref;
 	*buffer_out = refname_end + 1;
@@ -496,9 +471,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id)
 
 	git_oid_cpy(&ref->target.oid, id);
 
-	ref->modified = 1;
-
-	return GIT_SUCCESS;
+	return reference_write(ref);
 }
 
 int git_reference_set_target(git_reference *ref, const char *target)
@@ -511,9 +484,7 @@ int git_reference_set_target(git_reference *ref, const char *target)
 	if (ref->target.ref == NULL)
 		return GIT_ENOMEM;
 
-	ref->modified = 1;
-
-	return GIT_SUCCESS;
+	return reference_write(ref);
 }
 
 const git_oid *git_reference_oid(git_reference *ref)
@@ -528,6 +499,8 @@ const git_oid *git_reference_oid(git_reference *ref)
 
 const char *git_reference_target(git_reference *ref)
 {
+	assert(ref);
+
 	if (ref->type != GIT_REF_SYMBOLIC)
 		return NULL;
 
@@ -576,19 +549,14 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
 	return GIT_ETOONESTEDSYMREF;
 }
 
-int git_reference_write(git_reference *ref)
+static int reference_write(git_reference *ref)
 {
 	git_filebuf file;
-	git_reference *looked_up_reference;
 	char ref_path[GIT_PATH_MAX];
 	int error, contents_size;
 	char *ref_contents = NULL;
 
-	if (ref->type == GIT_REF_INVALID || ref->type == GIT_REF_ANY)
-		return GIT_EINVALIDREFSTATE;
-
-	if (!ref->modified)
-		return GIT_SUCCESS;
+	assert(ref->type == GIT_REF_OID || ref->type == GIT_REF_SYMBOLIC);
 
 	git__joinpath(ref_path, ref->owner->path_repository, ref->name);
 
@@ -619,27 +587,25 @@ int git_reference_write(git_reference *ref)
 		strcat(ref_contents, ref->target.ref);
 	}
 
+	/* TODO: win32 carriage return when writing references in Windows? */
 	ref_contents[contents_size - 1] = '\n';
 
 	if ((error = git_filebuf_write(&file, ref_contents, contents_size)) < GIT_SUCCESS)
 		goto error_cleanup;
 
 	error = git_filebuf_commit(&file);
+	if (error < GIT_SUCCESS)
+		goto unlock;
 
-	looked_up_reference = git_hashtable_lookup(ref->owner->references.cache, ref->name);
-
-	if (looked_up_reference == NULL) {
-		error = git_hashtable_insert(ref->owner->references.cache, ref->name, ref);
-		if (error < GIT_SUCCESS)
-			goto cleanup;
-	}
+	error = git_hashtable_insert(ref->owner->references.cache, ref->name, ref);
+	if (error < GIT_SUCCESS)
+		goto unlock;
 
-	goto cleanup;
+	free(ref_contents);
+	return GIT_SUCCESS;
 
 unlock:
-	git_filelock_unlock(&lock);
-
-cleanup:
+	git_filebuf_cleanup(&lock);
 	free(ref_contents);
 	return error;
 }
diff --git a/src/refs.h b/src/refs.h
index 2e9f340..33bedc4 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -21,8 +21,7 @@ struct git_reference {
 	git_rtype type;
 	char *name;
 
-	unsigned packed:1,
-			 modified:1;
+	unsigned packed:1;
 
 	union {
 		char *ref;