references: introduce git_reference_foreach_glob()
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
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 2918215..2aa0ac2 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -258,7 +258,6 @@ GIT_EXTERN(int) git_reference_packall(git_repository *repo);
*/
GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
-
/**
* Perform an operation on each reference in the repository
*
@@ -324,6 +323,36 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref);
*/
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
+/**
+ * Loop over all the references and issue a callback for each one
+ * which name matches the given glob pattern.
+ *
+ * The processed references may be filtered by type, or using
+ * a bitwise OR of several types. Use the magic value
+ * `GIT_REF_LISTALL` to obtain all references, including
+ * packed ones.
+ *
+ * @param repo Repository where to find the references.
+ *
+ * @param list_flags Filtering flags for the reference
+ * listing.
+ *
+ * @param callback Callback to invoke per found reference.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_reference_foreach_glob(
+ git_repository *repo,
+ const char *glob,
+ unsigned int list_flags,
+ int (*callback)(
+ const char *reference_name,
+ void *payload),
+ void *payload
+);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/refs.c b/src/refs.c
index 1046857..ee076b3 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1764,3 +1764,40 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char *
git_reference_free(ref);
return res;
}
+
+struct glob_cb_data {
+ const char *glob;
+ int (*callback)(const char *, void *);
+ void *payload;
+};
+
+static int fromglob_cb(const char *reference_name, void *payload)
+{
+ struct glob_cb_data *data = (struct glob_cb_data *)payload;
+
+ if (!p_fnmatch(data->glob, reference_name, 0))
+ return data->callback(reference_name, data->payload);
+
+ return 0;
+}
+
+int git_reference_foreach_glob(
+ git_repository *repo,
+ const char *glob,
+ unsigned int list_flags,
+ int (*callback)(
+ const char *reference_name,
+ void *payload),
+ void *payload)
+{
+ struct glob_cb_data data;
+
+ assert(repo && glob && callback);
+
+ data.glob = glob;
+ data.callback = callback;
+ data.payload = payload;
+
+ return git_reference_foreach(
+ repo, list_flags, fromglob_cb, &data);
+}
diff --git a/tests-clar/refs/foreachglob.c b/tests-clar/refs/foreachglob.c
new file mode 100644
index 0000000..8bbcd71
--- /dev/null
+++ b/tests-clar/refs/foreachglob.c
@@ -0,0 +1,70 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_foreachglob__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create_oid(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_foreachglob__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static int count_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return 0;
+}
+
+static void assert_retrieval(const char *glob, unsigned int flags, int expected_count)
+{
+ int count = 0;
+
+ cl_git_pass(git_reference_foreach_glob(repo, glob, flags, count_cb, &count));
+
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_foreachglob__retrieve_all_refs(void)
+{
+ /* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */
+ assert_retrieval("*", GIT_REF_LISTALL, 16);
+}
+
+void test_refs_foreachglob__retrieve_remote_branches(void)
+{
+ assert_retrieval("refs/remotes/*", GIT_REF_LISTALL, 2);
+}
+
+void test_refs_foreachglob__retrieve_local_branches(void)
+{
+ assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 7);
+}
+
+void test_refs_foreachglob__retrieve_partially_named_references(void)
+{
+ /*
+ * refs/heads/packed-test, refs/heads/test
+ * refs/remotes/test/master, refs/tags/test
+ */
+
+ assert_retrieval("*test*", GIT_REF_LISTALL, 4);
+}