From 155aca2da79c8cae650c4e4f387a40d8f0a66527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 27 Feb 2012 21:17:13 +0100 Subject: [PATCH] revwalk: introduce pushing and hiding by glob git_revwalk_{push,hide}_glob() lets you push the OIDs of references that match the specified glob. This is the basics for what git.git does with the rev-list options --branches, --tags, --remotes and --glob. --- include/git2/revwalk.h | 30 +++++++++++++++ src/revwalk.c | 87 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h index 1af0e4291..020c898ca 100644 --- a/include/git2/revwalk.h +++ b/include/git2/revwalk.h @@ -101,6 +101,20 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker); */ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid); +/** + * Push matching references + * + * The OIDs pinted to by the references that match the given glob + * pattern will be pushed to the revision walker. + * + * A leading 'refs/' is implied it not present as well as a trailing + * '/ *' if the glob lacks '?', '*' or '['. + * + * @param walk the walker being used for the traversal + * @param glob the glob pattern references should match + * @return GIT_SUCCESS or an error code + */ +GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob); /** * Mark a commit (and its ancestors) uninteresting for the output. @@ -117,6 +131,22 @@ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid); */ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid); +/** + * Hide matching references. + * + * The OIDs pinted to by the references that match the given glob + * pattern and their ancestors will be hidden from the output on the + * revision walk. + * + * A leading 'refs/' is implied it not present as well as a trailing + * '/ *' if the glob lacks '?', '*' or '['. + * + * @param walk the walker being used for the traversal + * @param glob the glob pattern references should match + * @return GIT_SUCCESS or an error code + */ +GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob); + /** * Get the next commit from the revision walk. * diff --git a/src/revwalk.c b/src/revwalk.c index 49d4b7236..8f818b814 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -13,6 +13,8 @@ #include "git2/revwalk.h" +#include + typedef struct commit_object { git_oid oid; uint32_t time; @@ -298,12 +300,97 @@ int git_revwalk_push(git_revwalk *walk, const git_oid *oid) return push_commit(walk, oid, 0); } + int git_revwalk_hide(git_revwalk *walk, const git_oid *oid) { assert(walk && oid); return push_commit(walk, oid, 1); } +struct push_cb_data { + git_revwalk *walk; + const char *glob; + int hide; +}; + +static int push_glob_cb(const char *refname, void *data_) +{ + struct push_cb_data *data = (struct push_cb_data *)data_; + + if (!git__fnmatch(data->glob, refname, 0)) { + git_reference *ref, *resolved; + int error; + + error = git_reference_lookup(&ref, data->walk->repo, refname); + if (error < GIT_SUCCESS) + return error; + error = git_reference_resolve(&resolved, ref); + git_reference_free(ref); + if (error < GIT_SUCCESS) + return error; + error = push_commit(data->walk, git_reference_oid(resolved), data->hide); + git_reference_free(resolved); + return error; + } + + return GIT_SUCCESS; +} + +static int push_glob(git_revwalk *walk, const char *glob, int hide) +{ + git_buf buf = GIT_BUF_INIT; + struct push_cb_data data; + int error; + regex_t preg; + + assert(walk && glob); + + /* refs/ is implied if not given in the glob */ + if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) { + git_buf_printf(&buf, GIT_REFS_DIR "%s", glob); + } else { + git_buf_puts(&buf, glob); + } + + /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ + memset(&preg, 0x0, sizeof(regex_t)); + if (regcomp(&preg, "[?*[]", REG_EXTENDED)) { + error = git__throw(GIT_EOSERR, "Regex failed to compile"); + goto cleanup; + } + + if (regexec(&preg, glob, 0, NULL, 0)) + git_buf_puts(&buf, "/*"); + + if (git_buf_oom(&buf)) { + error = GIT_ENOMEM; + goto cleanup; + } + + data.walk = walk; + data.glob = git_buf_cstr(&buf); + data.hide = hide; + + error = git_reference_foreach(walk->repo, GIT_REF_LISTALL, push_glob_cb, &data); + +cleanup: + regfree(&preg); + git_buf_free(&buf); + return error; +} + +int git_revwalk_push_glob(git_revwalk *walk, const char *glob) +{ + assert(walk && glob); + return push_glob(walk, glob, 0); +} + +int git_revwalk_hide_glob(git_revwalk *walk, const char *glob) +{ + assert(walk && glob); + return push_glob(walk, glob, 1); +} + static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit) { return git_pqueue_insert(&walk->iterator_time, commit);