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.
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
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index 1af0e42..020c898 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.
@@ -118,6 +132,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.
*
* The initial call to this method is *not* blocking when
diff --git a/src/revwalk.c b/src/revwalk.c
index 49d4b72..8f818b8 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -13,6 +13,8 @@
#include "git2/revwalk.h"
+#include <regex.h>
+
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);