Add new method `git_reference_listall` Lists all the references in a repository. Listing may be filtered by reference type. This should applease Lord Clem.
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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
diff --git a/include/git2/common.h b/include/git2/common.h
index 34efe80..11a08f8 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -27,6 +27,7 @@
#include "thread-utils.h"
#include <time.h>
+#include <stdlib.h>
#ifdef __cplusplus
# define GIT_BEGIN_DECL extern "C" {
@@ -158,6 +159,21 @@
#define GIT_EINVALIDREFSTATE (GIT_ERROR - 21)
GIT_BEGIN_DECL
+
+typedef struct {
+ char **strings;
+ size_t count;
+} git_strarray;
+
+GIT_INLINE(void) git_strarray_free(git_strarray *array)
+{
+ size_t i;
+ for (i = 0; i < array->count; ++i)
+ free(array->strings[i]);
+
+ free(array->strings);
+}
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 1702d7e..4ffc5ce 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -218,6 +218,29 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
*/
GIT_EXTERN(int) git_reference_packall(git_repository *repo);
+/**
+ * Fill a list with all the references that can be found
+ * in a repository.
+ *
+ * The listed 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.
+ *
+ * The string array will be filled with the names of all
+ * references; these values are owned by the user and
+ * should be free'd manually when no longer needed, using
+ * `git_strarray_free`.
+ *
+ * @param array Pointer to a git_strarray structure where
+ * the reference names will be stored
+ * @param repo Repository where to find the refs
+ * @param list_flags Filtering flags for the reference
+ * listing.
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/types.h b/include/git2/types.h
index 62467ec..b5a8d7b 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -145,6 +145,7 @@ typedef enum {
GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */
GIT_REF_PACKED = 4,
GIT_REF_HAS_PEEL = 8,
+ GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
} git_rtype;
/** @} */
diff --git a/src/refs.c b/src/refs.c
index 2fc383e..93897a7 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -235,6 +235,24 @@ static int loose_read(gitfo_buf *file_content, const char *name, const char *rep
return error;
}
+static git_rtype loose_guess_rtype(const char *full_path)
+{
+ gitfo_buf ref_file = GITFO_BUF_INIT;
+ git_rtype type;
+
+ type = GIT_REF_INVALID;
+
+ if (gitfo_read_file(&ref_file, full_path) == GIT_SUCCESS) {
+ if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
+ type = GIT_REF_SYMBOLIC;
+ else
+ type = GIT_REF_OID;
+ }
+
+ gitfo_free_buf(&ref_file);
+ return type;
+}
+
static int loose_lookup(
git_reference **ref_out,
git_repository *repo,
@@ -531,6 +549,31 @@ cleanup:
return error;
}
+
+
+
+struct dirent_list_data {
+ git_vector ref_list;
+ size_t repo_path_len;
+ unsigned int list_flags;
+};
+
+static int _dirent_loose_listall(void *_data, char *full_path)
+{
+ struct dirent_list_data *data = (struct dirent_list_data *)_data;
+ char *file_path;
+
+ if (gitfo_isdir(full_path) == GIT_SUCCESS)
+ return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data);
+
+ if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
+ return GIT_SUCCESS; /* we are filtering out this reference */
+
+ file_path = full_path + data->repo_path_len;
+
+ return git_vector_insert(&data->ref_list, git__strdup(file_path));
+}
+
static int _dirent_loose_load(void *data, char *full_path)
{
git_repository *repository = (git_repository *)data;
@@ -1292,6 +1335,45 @@ int git_reference_packall(git_repository *repo)
return packed_write(repo);
}
+int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags)
+{
+ int error;
+ struct dirent_list_data data;
+ char refs_path[GIT_PATH_MAX];
+
+ array->strings = NULL;
+ array->count = 0;
+
+ git_vector_init(&data.ref_list, 8, NULL);
+ data.repo_path_len = strlen(repo->path_repository);
+ data.list_flags = list_flags;
+
+ git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR);
+ error = gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
+
+ if (error < GIT_SUCCESS) {
+ git_vector_free(&data.ref_list);
+ return error;
+ }
+
+ if (list_flags & GIT_REF_PACKED) {
+ const char *ref_name;
+ void *_unused;
+
+ if ((error = packed_load(repo)) < GIT_SUCCESS) {
+ git_vector_free(&data.ref_list);
+ return error;
+ }
+
+ GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
+ git_vector_insert(&data.ref_list, git__strdup(ref_name));
+ );
+ }
+
+ array->strings = (char **)data.ref_list.contents;
+ array->count = data.ref_list.length;
+ return GIT_SUCCESS;
+}
diff --git a/tests/t10-refs.c b/tests/t10-refs.c
index abe3641..c70fb69 100644
--- a/tests/t10-refs.c
+++ b/tests/t10-refs.c
@@ -710,6 +710,18 @@ BEGIN_TEST(normalize2, "tests borrowed from JGit")
must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1.hour.ago}", NULL));
END_TEST
+BEGIN_TEST(list0, "try to list all the references in our test repo")
+ git_repository *repo;
+ git_strarray ref_list;
+
+ must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(git_reference_listall(&ref_list, repo, GIT_REF_LISTALL));
+ must_be_true(ref_list.count == 8); /* 8 refs in total if we include the packed ones */
+
+ git_strarray_free(&ref_list);
+ git_repository_free(repo);
+END_TEST
+
BEGIN_SUITE(refs)
ADD_TEST(readtag0);
@@ -741,4 +753,5 @@ BEGIN_SUITE(refs)
ADD_TEST(rename4);
ADD_TEST(delete0);
+ ADD_TEST(list0);
END_SUITE