diff --git a/include/git2/revparse.h b/include/git2/revparse.h index 6edb7767c..edd8b3cce 100644 --- a/include/git2/revparse.h +++ b/include/git2/revparse.h @@ -32,6 +32,19 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec); +/** + * Parse a string with the form of a revision range, as accepted by + * `git rev-list`, `git diff`, and others. + * + * @param left (output) the left-hand commit + * @param right (output) the right-hand commit + * @param threedots (output) 0 if the endpoints are separated by two dots, 1 if by three + * @param repo the repository to find the commits in + * @param rangelike the rangelike string to be parsed + * @return 0 on success, or any error `git_revparse_single` can return + */ +GIT_EXTERN(int) git_revparse_rangelike(git_object **left, git_object **right, int *threedots, git_repository *repo, const char *rangelike); + /** @} */ GIT_END_DECL #endif diff --git a/src/revparse.c b/src/revparse.c index 884879975..7f1497130 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -866,3 +866,28 @@ cleanup: git_buf_free(&buf); return error; } + +int git_revparse_rangelike(git_object **left, git_object **right, int *threedots, git_repository *repo, const char *rangelike) +{ + int error = 0; + const char *p, *q; + char *revspec; + + p = strstr(rangelike, ".."); + if (!p) { + giterr_set(GITERR_INVALID, "Malformed range (or rangelike syntax): %s", rangelike); + return GIT_EINVALIDSPEC; + } else if (p[2] == '.') { + *threedots = 1; + q = p + 3; + } else { + *threedots = 0; + q = p + 2; + } + + revspec = git__substrdup(rangelike, p - rangelike); + error = (git_revparse_single(left, repo, revspec) + || git_revparse_single(right, repo, q)); + git__free(revspec); + return error; +} diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index be92c1956..b8d7d5edc 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -32,6 +32,33 @@ static void test_object(const char *spec, const char *expected_oid) test_object_inrepo(spec, expected_oid, g_repo); } +static void test_rangelike(const char *rangelike, + const char *expected_left, + const char *expected_right, + int expected_threedots) +{ + char objstr[64] = {0}; + git_object *left, *right; + int threedots; + int error; + + error = git_revparse_rangelike(&left, &right, &threedots, g_repo, rangelike); + + if (expected_left != NULL) { + cl_assert_equal_i(0, error); + cl_assert_equal_i(threedots, expected_threedots); + git_oid_fmt(objstr, git_object_id(left)); + cl_assert_equal_s(objstr, expected_left); + git_oid_fmt(objstr, git_object_id(right)); + cl_assert_equal_s(objstr, expected_right); + } else + cl_assert(error != 0); + + git_object_free(left); + git_object_free(right); +} + + void test_refs_revparse__initialize(void) { cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); @@ -595,3 +622,19 @@ void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void) git_object_free(target); cl_git_sandbox_cleanup(); } + + +void test_refs_revparse__range(void) +{ + test_rangelike("be3563a^1..be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + 0); + + test_rangelike("be3563a^1...be3563a", + "9fd738e8f7967c078dceed8190330fc8648ee56a", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + 1); + + test_rangelike("be3563a^1.be3563a", NULL, NULL, 0); +}