From 005718280712634486a097427212e652b0e29f36 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Sat, 12 Mar 2011 16:04:46 +0200 Subject: [PATCH] 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. --- include/git2/common.h | 16 +++++++++ include/git2/refs.h | 23 ++++++++++++ include/git2/types.h | 1 + src/refs.c | 82 +++++++++++++++++++++++++++++++++++++++++++ tests/t10-refs.c | 13 +++++++ 5 files changed, 135 insertions(+) diff --git a/include/git2/common.h b/include/git2/common.h index 34efe808b..11a08f897 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -27,6 +27,7 @@ #include "thread-utils.h" #include +#include #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 1702d7ee1..4ffc5ce5b 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 62467ec45..b5a8d7b2d 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 2fc383e22..93897a7f9 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 abe364133..c70fb69ce 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