Commit c58cac12c24fbb127cf1928bec20decb007a75e8

Carlos Martín Nieto 2013-05-04T18:06:14

Introduce a glob-filtering iterator If the backend doesn't provide support for it, the matching is done in refdb on top of a normal iterator.

diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index f5eacd1..8dbf38c 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -30,10 +30,11 @@ GIT_BEGIN_DECL
  *             ...
  *     }
  *
- * and assing `iter->parent.backend` to your `git_refdb_backend`.
+ * and assign `iter->parent.backend` to your `git_refdb_backend`.
  */
 struct git_reference_iterator {
 	git_refdb_backend *backend;
+	char *glob;
 };
 
 /** An instance for a custom backend */
@@ -68,6 +69,17 @@ struct git_refdb_backend {
 		struct git_refdb_backend *backend);
 
 	/**
+	 * Allocate a glob-filtering iterator object for the backend.
+	 *
+	 * A refdb implementation may provide this function. If it's
+	 * not available, the glob matching will be done by the frontend.
+	 */
+	int (*iterator_glob)(
+		git_reference_iterator **iter,
+		struct git_refdb_backend *backend,
+		const char *glob);
+
+	/**
 	 * Return the current value and advance the iterator.
 	 *
 	 * A refdb implementation must provide this function.
diff --git a/src/refdb.c b/src/refdb.c
index 5e33c2e..9f9037c 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -126,29 +126,59 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
 
 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db)
 {
-	git_reference_iterator *iter;
-
 	if (!db->backend || !db->backend->iterator) {
 		giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators");
 		return -1;
 	}
 
-	if (db->backend->iterator(&iter, db->backend) < 0) {
-		git__free(iter);
+	if (db->backend->iterator(out, db->backend) < 0)
+		return -1;
+
+	return 0;
+}
+
+int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob)
+{
+	if (!db->backend) {
+		giterr_set(GITERR_REFERENCE, "There are no backends loaded");
+		return -1;
+	}
+
+	if (db->backend->iterator_glob)
+		return db->backend->iterator_glob(out, db->backend, glob);
+
+	/* If the backend doesn't support glob-filtering themselves, we have to do it */
+	if (db->backend->iterator(out, db->backend) < 0)
+		return -1;
+
+	(*out)->glob = git__strdup(glob);
+	if (!(*out)->glob) {
+		db->backend->iterator_free(*out);
 		return -1;
 	}
 
-	*out = iter;
 	return 0;
 }
 
 int git_refdb_next(const char **out, git_reference_iterator *iter)
 {
-	return iter->backend->next(out, iter);
+	int error;
+
+	if (!iter->glob)
+		return iter->backend->next(out, iter);
+
+	/* If the iterator has a glob, we need to filter */
+	while ((error = iter->backend->next(out, iter)) == 0) {
+		if (!p_fnmatch(iter->glob, *out, 0))
+			break;
+	}
+
+	return error;
 }
 
 void git_refdb_iterator_free(git_reference_iterator *iter)
 {
+	git__free(iter->glob);
 	iter->backend->iterator_free(iter);
 }
 
diff --git a/src/refdb.h b/src/refdb.h
index e88dead..2edd05d 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -27,6 +27,7 @@ int git_refdb_lookup(
 	const char *ref_name);
 
 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db);
+int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob);
 int git_refdb_next(const char **out, git_reference_iterator *iter);
 void git_refdb_iterator_free(git_reference_iterator *iter);
 
diff --git a/src/refs.c b/src/refs.c
index a7be117..fc6652f 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -593,6 +593,16 @@ int git_reference_iterator_new(git_reference_iterator **out, git_repository *rep
 	return git_refdb_iterator(out, refdb);
 }
 
+int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob)
+{
+	git_refdb *refdb;
+
+	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
+		return -1;
+
+	return git_refdb_iterator_glob(out, refdb, glob);
+}
+
 int git_reference_next(const char **out, git_reference_iterator *iter)
 {
 	return git_refdb_next(out, iter);
@@ -928,13 +938,10 @@ int git_reference_foreach_glob(
 	const char *name;
 	int error;
 
-	if (git_reference_iterator_new(&iter, repo) < 0)
+	if (git_reference_iterator_glob_new(&iter, repo, glob) < 0)
 		return -1;
 
 	while ((error = git_reference_next(&name, iter)) == 0) {
-		if (p_fnmatch(glob, name, 0))
-			continue;
-
 		if (callback(name, payload)) {
 			error = GIT_EUSER;
 			goto out;
diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c
index 4d11856..961e18d 100644
--- a/tests-clar/refdb/testdb.c
+++ b/tests-clar/refdb/testdb.c
@@ -123,7 +123,7 @@ static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_
 
 	GIT_UNUSED(_backend);
 
-	iter = git__malloc(sizeof(refdb_test_iter));
+	iter = git__calloc(1, sizeof(refdb_test_iter));
 	GITERR_CHECK_ALLOC(iter);
 
 	iter->parent.backend = _backend;