tag: Add git_tag_peel() which recursively peel a tag until a non tag git_object is met
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
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);