Implement unified git_revparse
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 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
index 71ff6d6..9315b66 100644
--- a/include/git2/revparse.h
+++ b/include/git2/revparse.h
@@ -51,35 +51,37 @@ GIT_EXTERN(int) git_revparse_rangelike(git_object **left, git_object **right, in
* git_revparse.
*/
typedef enum {
- /** The spec targeted a single object. */
- GIT_REVPARSE_SINGLE = 1 << 0,
- /** The spec targeted a range of commits. */
- GIT_REVPARSE_RANGE = 1 << 1,
- /** The spec used the '...' operator, which invokes special semantics. */
- GIT_REVPARSE_MERGE_BASE = 1 << 2,
+ /** The spec targeted a single object. */
+ GIT_REVPARSE_SINGLE = 1 << 0,
+ /** The spec targeted a range of commits. */
+ GIT_REVPARSE_RANGE = 1 << 1,
+ /** The spec used the '...' operator, which invokes special semantics. */
+ GIT_REVPARSE_MERGE_BASE = 1 << 2,
} git_revparse_flag_t;
/**
- * Find an object or range of commits as specified by a revision string.
- * See `man gitrevisions` or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions
- * for information on the syntax accepted.
+ * Parse a revision string for left, right, and intent. See `man gitrevisions` or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for information
+ * on the syntax accepted.
*
* @param left buffer that receives the target of the left side of a range operator. If
* there is no range operator, this buffer receives the single target.
* @param right buffer that receives the target of the right side of a range operator.
- * This is only filled in if `spec` specifies a range of commits
- * @param flags buffer that receives a bitwise combination of `git_revparse_flag_t` values
+ * This is only filled in if `spec` specifies a range of commits. May
+ * be NULL.
+ * @param flags buffer that receives a bitwise combination of `git_revparse_flag_t` values.
+ * May be NULL.
* @param repo the repository to search in
* @param spec the rev-parse spec to parse
* @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
*/
GIT_EXTERN(int) git_revparse(
- git_oid *left,
- git_oid *right,
- unsigned int *flags,
- git_repository *repo,
- const char *spec);
+ git_oid *left,
+ git_oid *right,
+ unsigned int *flags,
+ git_repository *repo,
+ const char *spec);
/** @} */
diff --git a/src/revparse.c b/src/revparse.c
index 2ba2938..2ba42d8 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -107,7 +107,7 @@ static int build_regex(regex_t *regex, const char *pattern)
error = regcomp(regex, pattern, REG_EXTENDED);
if (!error)
return 0;
-
+
error = giterr_set_regex(regex, error);
regfree(regex);
@@ -125,7 +125,7 @@ static int maybe_describe(git_object**out, git_repository *repo, const char *spe
if (substr == NULL)
return GIT_ENOTFOUND;
-
+
if (build_regex(®ex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
return -1;
@@ -358,7 +358,7 @@ static int retrieve_remote_tracking_reference(git_reference **base_ref, const ch
if ((error = git_branch_tracking(&tracking, ref)) < 0)
goto cleanup;
-
+
*base_ref = tracking;
cleanup:
@@ -508,7 +508,7 @@ static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
int error;
git_oid oid;
git_object *obj;
-
+
while (!(error = git_revwalk_next(&oid, walk))) {
error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJ_COMMIT);
@@ -537,7 +537,7 @@ static int handle_grep_syntax(git_object **out, git_repository *repo, const git_
if ((error = build_regex(&preg, pattern)) < 0)
return error;
-
+
if ((error = git_revwalk_new(&walk, repo)) < 0)
goto cleanup;
@@ -551,7 +551,7 @@ static int handle_grep_syntax(git_object **out, git_repository *repo, const git_
goto cleanup;
error = walk_and_search(out, walk, &preg);
-
+
cleanup:
regfree(&preg);
git_revwalk_free(walk);
@@ -892,3 +892,55 @@ int git_revparse_rangelike(git_object **left, git_object **right, int *threedots
git__free(revspec);
return error;
}
+
+
+int git_revparse(
+ git_oid *left,
+ git_oid *right,
+ unsigned int *flags,
+ git_repository *repo,
+ const char *spec)
+{
+ unsigned int lflags = 0;
+ const char *dotdot;
+ int error = 0;
+ git_object *obj = NULL;
+
+ assert(left && repo && spec);
+
+ if ((dotdot = strstr(spec, "..")) != NULL) {
+ char *lstr;
+ const char *rstr;
+ lflags = GIT_REVPARSE_RANGE;
+
+ lstr = git__substrdup(spec, dotdot-spec);
+ rstr = dotdot + 2;
+ if (dotdot[2] == '.') {
+ lflags |= GIT_REVPARSE_MERGE_BASE;
+ rstr++;
+ }
+
+ if (!(error = git_revparse_single(&obj, repo, lstr))) {
+ git_oid_cpy(left, git_object_id(obj));
+ git_object_free(obj);
+ }
+ if (right && !(error = git_revparse_single(&obj, repo, rstr))) {
+ git_oid_cpy(right, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ git__free((void*)lstr);
+ } else {
+ lflags = GIT_REVPARSE_SINGLE;
+ if (!(error = git_revparse_single(&obj, repo, spec))) {
+ git_oid_cpy(left, git_object_id(obj));
+ git_object_free(obj);
+ }
+ }
+
+ if (flags)
+ *flags = lflags;
+
+ return error;
+}
+
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 66ee391..ab8839f 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -27,6 +27,37 @@ static void test_object_inrepo(const char *spec, const char *expected_oid, git_r
git_object_free(obj);
}
+static void test_id_inrepo(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_flag_t expected_flags,
+ git_repository *repo)
+{
+ git_oid l = {{0}}, r = {{0}};
+ git_revparse_flag_t flags = 0;
+
+ int error = git_revparse(&l, &r, &flags, repo, spec);
+
+ if (expected_left) {
+ char str[64] = {0};
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(str, &l);
+ cl_assert_equal_s(str, expected_left);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+ }
+
+ if (expected_right) {
+ char str[64] = {0};
+ git_oid_fmt(str, &r);
+ cl_assert_equal_s(str, expected_right);
+ }
+
+ if (expected_flags)
+ cl_assert_equal_i(expected_flags, flags);
+}
+
static void test_object(const char *spec, const char *expected_oid)
{
test_object_inrepo(spec, expected_oid, g_repo);
@@ -59,6 +90,15 @@ static void test_rangelike(const char *rangelike,
}
+static void test_id(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_flag_t expected_flags)
+{
+ test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
+}
+
void test_refs_revparse__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
@@ -639,3 +679,22 @@ void test_refs_revparse__range(void)
test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
}
+
+void test_refs_revparse__validates_args(void)
+{
+ git_oid l={{0}}, r={{0}};
+ git_revparse_flag_t flags = 0;
+
+ cl_git_pass(git_revparse(&l,&r,NULL, g_repo, "HEAD"));
+ cl_git_pass(git_revparse(&l,NULL,&flags, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_revparse(&l,&r,&flags, g_repo, "^&*("));
+}
+
+void test_refs_revparse__parses_range_operator(void)
+{
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVPARSE_SINGLE);
+ test_id("HEAD~3..HEAD", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_REVPARSE_RANGE);
+ test_id("HEAD~3...HEAD", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+}
+