Commit 2b5af615e1f4344f6073d0ddf3e83256d9a8d58f

nulltoken 2011-07-07T13:47:45

tag: add pattern based retrieval of list of tag names

diff --git a/include/git2/tag.h b/include/git2/tag.h
index 15635cc..71b27bb 100644
--- a/include/git2/tag.h
+++ b/include/git2/tag.h
@@ -235,6 +235,29 @@ GIT_EXTERN(int) git_tag_list(
 		git_strarray *tag_names,
 		git_repository *repo);
 
+/**
+ * Fill a list with all the tags in the Repository
+ * which name match a defined pattern
+ *
+ * If an empty pattern is provided, all the tags
+ * will be returned.
+ *
+ * The string array will be filled with the names of the
+ * matching tags; these values are owned by the user and
+ * should be free'd manually when no longer needed, using
+ * `git_strarray_free`.
+ *
+ * @param tag_names Pointer to a git_strarray structure where
+ *		the tag names will be stored
+ * @param pattern Standard fnmatch pattern
+ * @param repo Repository where to find the tags
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_tag_list_match(
+		git_strarray *tag_names,
+		const char *pattern,
+		git_repository *repo);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/tag.c b/src/tag.c
index de70c50..f508fb7 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -364,23 +364,42 @@ int git_tag__parse(git_tag *tag, git_odb_object *obj)
 	return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len);
 }
 
+typedef struct {
+  git_vector *taglist;
+  const char *pattern;
+} tag_filter_data;
+
+#define GIT_REFS_TAGS_DIR_LEN STRLEN(GIT_REFS_TAGS_DIR)
+
 static int tag_list_cb(const char *tag_name, void *payload)
 {
-	if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) == 0)
-		return git_vector_insert((git_vector *)payload, git__strdup(tag_name));
+	tag_filter_data *filter;
+
+	if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)
+		return GIT_SUCCESS;
+
+	filter = (tag_filter_data *)payload;
+	if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == GIT_SUCCESS)
+		return git_vector_insert(filter->taglist, git__strdup(tag_name));
 
 	return GIT_SUCCESS;
 }
 
-int git_tag_list(git_strarray *tag_names, git_repository *repo)
+int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
 {
 	int error;
+	tag_filter_data filter;
 	git_vector taglist;
 
+	assert(tag_names && repo && pattern);
+
 	if (git_vector_init(&taglist, 8, NULL) < GIT_SUCCESS)
 		return GIT_ENOMEM;
 
-	error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&taglist);
+	filter.taglist = &taglist;
+	filter.pattern = pattern;
+
+	error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);
 	if (error < GIT_SUCCESS) {
 		git_vector_free(&taglist);
 		return git__rethrow(error, "Failed to list tags");
@@ -390,3 +409,8 @@ int git_tag_list(git_strarray *tag_names, git_repository *repo)
 	tag_names->count = taglist.length;
 	return GIT_SUCCESS;
 }
+
+int git_tag_list(git_strarray *tag_names, git_repository *repo)
+{
+	return git_tag_list_match(tag_names, "", repo);
+}
\ No newline at end of file
diff --git a/tests/t08-tag.c b/tests/t08-tag.c
index 64a939f..aeff8b3 100644
--- a/tests/t08-tag.c
+++ b/tests/t08-tag.c
@@ -77,6 +77,35 @@ BEGIN_TEST(read1, "list all tag names from the repository")
 	git_repository_free(repo);
 END_TEST
 
+static int ensure_tag_pattern_match(git_repository *repo, const char *pattern, const size_t expected_matches)
+{
+	git_strarray tag_list;
+	int error = GIT_SUCCESS;
+
+	if ((error = git_tag_list_match(&tag_list, pattern, repo)) < GIT_SUCCESS)
+		goto exit;
+
+	if (tag_list.count != expected_matches)
+		error = GIT_ERROR;
+
+exit:
+	git_strarray_free(&tag_list);
+	return error;
+}
+
+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, "t*", 1));
+	must_pass(ensure_tag_pattern_match(repo, "*b", 2));
+	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));
+	git_repository_free(repo);
+END_TEST
+
 
 #define TAGGER_NAME "Vicent Marti"
 #define TAGGER_EMAIL "vicent@github.com"
@@ -222,6 +251,8 @@ END_TEST
 BEGIN_SUITE(tag)
 	ADD_TEST(read0);
 	ADD_TEST(read1);
+	ADD_TEST(read2);
+
 	ADD_TEST(write0);
 	ADD_TEST(write2);
 	ADD_TEST(write3);