Commit 3f46f313cbfcf57e5cbf3d7dc55b747568a21bef

nulltoken 2012-04-06T14:34:26

tag: Add git_tag_peel() which recursively peel a tag until a non tag git_object is met

diff --git a/include/git2/tag.h b/include/git2/tag.h
index f7fce3a..9ab4b7b 100644
--- a/include/git2/tag.h
+++ b/include/git2/tag.h
@@ -274,6 +274,21 @@ GIT_EXTERN(int) git_tag_list_match(
 		const char *pattern,
 		git_repository *repo);
 
+/**
+ * Recursively peel a tag until a non tag git_object
+ * is met
+ *
+ * The retrieved `tag_target` object is owned by the repository
+ * and should be closed with the `git_object_free` method.
+ *
+ * @param tag_target Pointer to the peeled git_object
+ * @param tag The tag to be processed
+ * @return GIT_SUCCESS or an error code
+ */
+GIT_EXTERN(int) git_tag_peel(
+		git_object **tag_target,
+		git_tag *tag);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/tag.c b/src/tag.c
index a5089e7..cfd2c70 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -451,3 +451,23 @@ int git_tag_list(git_strarray *tag_names, git_repository *repo)
 {
 	return git_tag_list_match(tag_names, "", repo);
 }
+
+int git_tag_peel(git_object **tag_target, git_tag *tag)
+{
+	int error;
+	git_object *target;
+
+	assert(tag_target && tag);
+
+	if (git_tag_target(&target, tag) < 0)
+		return -1;
+
+	if (git_object_type(target) == GIT_OBJ_TAG) {
+		error = git_tag_peel(tag_target, (git_tag *)target);
+		git_object_free(target);
+		return error;
+	}
+
+	*tag_target = target;
+	return 0;
+}
diff --git a/tests-clar/network/remotelocal.c b/tests-clar/network/remotelocal.c
index 81af777..74a0b57 100644
--- a/tests-clar/network/remotelocal.c
+++ b/tests-clar/network/remotelocal.c
@@ -88,7 +88,7 @@ void test_network_remotelocal__retrieve_advertised_references(void)
 
 	cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
 
-	cl_assert(how_many_refs == 12); /* 1 HEAD + 9 refs + 2 peeled tags */
+	cl_assert(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
 }
 
 void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void)
@@ -102,7 +102,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi
 
 	cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
 
-	cl_assert(how_many_refs == 12); /* 1 HEAD */
+	cl_assert(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
 
 	cl_fixture_cleanup("spaced testrepo.git");
 }
diff --git a/tests-clar/object/tag/peel.c b/tests-clar/object/tag/peel.c
new file mode 100644
index 0000000..97c5a7d
--- /dev/null
+++ b/tests-clar/object/tag/peel.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+#include "tag.h"
+
+static git_repository *repo;
+static git_tag *tag;
+static git_object *target;
+
+void test_object_tag_peel__initialize(void)
+{
+	cl_fixture_sandbox("testrepo.git");
+	cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_object_tag_peel__cleanup(void)
+{
+	git_tag_free(tag);
+	git_object_free(target);
+	git_repository_free(repo);
+
+	cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_tag_from_oid(git_tag **tag_out, git_repository *repo, const char *sha)
+{
+	git_oid oid;
+	
+	cl_git_pass(git_oid_fromstr(&oid, sha));
+	cl_git_pass(git_tag_lookup(tag_out, repo, &oid));
+}
+
+void test_object_tag_peel__can_peel_to_a_commit(void)
+{
+	retrieve_tag_from_oid(&tag, repo, "7b4384978d2493e851f9cca7858815fac9b10980");
+
+	cl_git_pass(git_tag_peel(&target, tag));
+	cl_assert(git_object_type(target) == GIT_OBJ_COMMIT);
+	cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_several_nested_tags_to_a_commit(void)
+{
+	retrieve_tag_from_oid(&tag, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+	cl_git_pass(git_tag_peel(&target, tag));
+	cl_assert(git_object_type(target) == GIT_OBJ_COMMIT);
+	cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_to_a_non_commit(void)
+{
+	retrieve_tag_from_oid(&tag, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+
+	cl_git_pass(git_tag_peel(&target, tag));
+	cl_assert(git_object_type(target) == GIT_OBJ_BLOB);
+	cl_git_pass(git_oid_streq(git_object_id(target), "1385f264afb75a56a5bec74243be9b367ba4ca08"));	
+}
diff --git a/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 0000000..351cff8
Binary files /dev/null and b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 differ
diff --git a/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
new file mode 100644
index 0000000..6c146d6
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
@@ -0,0 +1 @@
+521d87c1ec3aef9824daf6d96cc0ae3710766d91
diff --git a/tests/t08-tag.c b/tests/t08-tag.c
index 4cbd483..1586be1 100644
--- a/tests/t08-tag.c
+++ b/tests/t08-tag.c
@@ -73,7 +73,7 @@ BEGIN_TEST(read1, "list all tag names from the repository")
 	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
 	must_pass(git_tag_list(&tag_list, repo));
 
-	must_be_true(tag_list.count == 3);
+	must_be_true(tag_list.count == 4);
 
 	git_strarray_free(&tag_list);
 	git_repository_free(repo);
@@ -98,10 +98,10 @@ exit:
 BEGIN_TEST(read2, "list all tag names from the repository matching a specified pattern")
 	git_repository *repo;
 	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-	must_pass(ensure_tag_pattern_match(repo, "", 3));
-	must_pass(ensure_tag_pattern_match(repo, "*", 3));
+	must_pass(ensure_tag_pattern_match(repo, "", 4));
+	must_pass(ensure_tag_pattern_match(repo, "*", 4));
 	must_pass(ensure_tag_pattern_match(repo, "t*", 1));
-	must_pass(ensure_tag_pattern_match(repo, "*b", 2));
+	must_pass(ensure_tag_pattern_match(repo, "*b", 3));
 	must_pass(ensure_tag_pattern_match(repo, "e", 0));
 	must_pass(ensure_tag_pattern_match(repo, "e90810b", 1));
 	must_pass(ensure_tag_pattern_match(repo, "e90810[ab]", 1));
diff --git a/tests/t10-refs.c b/tests/t10-refs.c
index ad88172..7229ded 100644
--- a/tests/t10-refs.c
+++ b/tests/t10-refs.c
@@ -1164,10 +1164,10 @@ BEGIN_TEST(list0, "try to list all the references in our test repo")
 			printf("# %s\n", ref_list.strings[i]);
 	}*/
 
-	/* We have exactly 9 refs in total if we include the packed ones:
+	/* We have exactly 10 refs in total if we include the packed ones:
 	 * there is a reference that exists both in the packfile and as
 	 * loose, but we only list it once */
-	must_be_true(ref_list.count == 9);
+	must_be_true(ref_list.count == 10);
 
 	git_strarray_free(&ref_list);
 	git_repository_free(repo);