Commit c1b5e8c42bca585c2bc728a0583b20095bd8c128

nulltoken 2013-02-15T11:35:33

branch: Make git_branch_remote_name() cope with orphaned heads

diff --git a/include/git2/branch.h b/include/git2/branch.h
index 3c7fb44..d372c2c 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -221,7 +221,7 @@ GIT_EXTERN(int) git_branch_is_head(
  *
  * @param repo The repository where the branch lives.
  *
- * @param branch The reference to the remote tracking branch.
+ * @param canonical_branch_name name of the remote tracking branch.
  *
  * @return Number of characters in the reference name
  *     including the trailing NUL byte; GIT_ENOTFOUND
@@ -233,7 +233,7 @@ GIT_EXTERN(int) git_branch_remote_name(
 	char *remote_name_out,
 	size_t buffer_size,
 	git_repository *repo,
-	git_reference *branch);
+	const char *canonical_branch_name);
 
 /** @} */
 GIT_END_DECL
diff --git a/src/branch.c b/src/branch.c
index 11ecbe9..a503875 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -323,7 +323,7 @@ int git_branch_remote_name(
 	char *remote_name_out,
 	size_t buffer_size,
 	git_repository *repo,
-	git_reference *branch)
+	const char *canonical_branch_name)
 {
 	git_strarray remote_list = {0};
 	size_t i, remote_name_size;
@@ -332,15 +332,15 @@ int git_branch_remote_name(
 	int error = 0;
 	char *remote_name = NULL;
 
-	assert(branch);
+	assert(repo && canonical_branch_name);
 
 	if (remote_name_out && buffer_size)
 		*remote_name_out = '\0';
 
 	/* Verify that this is a remote branch */
-	if (!git_reference_is_remote(branch)) {
-		giterr_set(GITERR_INVALID,
-				   "Reference '%s' is not a remote branch.", branch->name);
+	if (!git_reference__is_remote(canonical_branch_name)) {
+		giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
+			canonical_branch_name);
 		error = GIT_ERROR;
 		goto cleanup;
 	}
@@ -358,7 +358,7 @@ int git_branch_remote_name(
 
 		/* Defensivly check that we have a fetchspec */
 		if (fetchspec &&
-			git_refspec_dst_matches(fetchspec, branch->name)) {
+			git_refspec_dst_matches(fetchspec, canonical_branch_name)) {
 			/* If we have not already set out yet, then set
 			 * it to the matching remote name. Otherwise
 			 * multiple remotes match this reference, and it
diff --git a/src/refs.c b/src/refs.c
index 866c230..cca3f3e 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1926,10 +1926,15 @@ int git_reference_is_branch(git_reference *ref)
 	return git_reference__is_branch(ref->name);
 }
 
+int git_reference__is_remote(const char *ref_name)
+{
+	return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
+}
+
 int git_reference_is_remote(git_reference *ref)
 {
 	assert(ref);
-	return git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0;
+	return git_reference__is_remote(ref->name);
 }
 
 static int peel_error(int error, git_reference *ref, const char* msg)
diff --git a/src/refs.h b/src/refs.h
index 1228cea..7bd1ae6 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -70,6 +70,7 @@ int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int f
 int git_reference__is_valid_name(const char *refname, unsigned int flags);
 int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name);
 int git_reference__is_branch(const char *ref_name);
+int git_reference__is_remote(const char *ref_name);
 
 /**
  * Lookup a reference by name and try to resolve to an OID.
diff --git a/tests-clar/clone/empty.c b/tests-clar/clone/empty.c
index e611bc2..0f86725 100644
--- a/tests-clar/clone/empty.c
+++ b/tests-clar/clone/empty.c
@@ -34,7 +34,9 @@ static void cleanup_repository(void *path)
 void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
 {
 	char *local_name = "refs/heads/master";
-	char tracking_name[1024];
+	const char *expected_tracked_branch_name = "refs/remotes/origin/master";
+	const char *expected_remote_name = "origin";
+	char buffer[1024];
 	git_reference *ref;
 
 	cl_set_cleanup(&cleanup_repository, "./empty");
@@ -46,8 +48,20 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
 	cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
 
 	/* ...one can still retrieve the name of the remote tracking reference */
-	cl_assert_equal_i((int)strlen("refs/remotes/origin/master") + 1U, 
-		git_branch_tracking_name(tracking_name, 1024, g_repo_cloned, local_name));
+	cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1, 
+		git_branch_tracking_name(buffer, 1024, g_repo_cloned, local_name));
+
+	cl_assert_equal_s(expected_tracked_branch_name, buffer);
+
+	/* ...and the name of the remote... */
+	cl_assert_equal_i((int)strlen(expected_remote_name) + 1, 
+		git_branch_remote_name(buffer, 1024, g_repo_cloned, expected_tracked_branch_name));
+
+	cl_assert_equal_s(expected_remote_name, buffer);
+
+	/* ...even when the remote HEAD is orphaned as well */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
+		expected_tracked_branch_name));
 }
 
 void test_clone_empty__can_clone_an_empty_local_repo(void)
diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c
index be355af..5865261 100644
--- a/tests-clar/refs/branches/remote.c
+++ b/tests-clar/refs/branches/remote.c
@@ -35,9 +35,9 @@ void test_refs_branches_remote__can_get_remote_for_branch(void)
 	cl_assert_equal_s("test/master", name);
 
 	cl_assert_equal_i(expectedRemoteNameLength,
-		git_branch_remote_name(NULL, 0, g_repo, ref));
+		git_branch_remote_name(NULL, 0, g_repo, git_reference_name(ref)));
 	cl_assert_equal_i(expectedRemoteNameLength,
-		git_branch_remote_name(remotename, expectedRemoteNameLength, g_repo, ref));
+		git_branch_remote_name(remotename, expectedRemoteNameLength, g_repo, git_reference_name(ref)));
 	cl_assert_equal_s("test", remotename);
 
 	git_reference_free(ref);
@@ -56,9 +56,9 @@ void test_refs_branches_remote__insufficient_buffer_returns_error(void)
 	cl_assert_equal_s("test/master", name);
 
 	cl_assert_equal_i(expectedRemoteNameLength,
-		git_branch_remote_name(NULL, 0, g_repo, ref));
+		git_branch_remote_name(NULL, 0, g_repo, git_reference_name(ref)));
 	cl_git_fail_with(GIT_ERROR,
-		git_branch_remote_name(remotename, expectedRemoteNameLength - 1, g_repo, ref));
+		git_branch_remote_name(remotename, expectedRemoteNameLength - 1, g_repo, git_reference_name(ref)));
 
 	git_reference_free(ref);
 }
@@ -78,7 +78,7 @@ void test_refs_branches_remote__no_matching_remote_returns_error(void)
 	cl_git_pass(git_branch_name(&name, ref));
 	cl_assert_equal_s("nonexistent/master", name);
 
-	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_ENOTFOUND);
+	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, git_reference_name(ref)), GIT_ENOTFOUND);
 	git_reference_free(ref);
 }
 
@@ -91,7 +91,7 @@ void test_refs_branches_remote__local_remote_returns_error(void)
 	cl_git_pass(git_branch_name(&name, ref));
 	cl_assert_equal_s("master",name);
 
-	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_ERROR);
+	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, git_reference_name(ref)), GIT_ERROR);
 	git_reference_free(ref);
 }
 
@@ -114,6 +114,6 @@ void test_refs_branches_remote__ambiguous_remote_returns_error(void)
 	cl_git_pass(git_branch_name(&name, ref));
 	cl_assert_equal_s("test/master", name);
 
-	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_EAMBIGUOUS);
+	cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, git_reference_name(ref)), GIT_EAMBIGUOUS);
 	git_reference_free(ref);
 }