Commit 5b3d52ce by Russell Belfer

Merge pull request #1568 from nulltoken/topic/revparse_ext

Introduce git_revparse_ext()
parents e3107e0e f672cd2a
......@@ -32,6 +32,28 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
/**
* Find a single object, 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.
*
* In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
* point to an intermediate reference. When such expressions are being passed
* in, `reference_out` will be valued as well.
*
* @param object_out pointer to output object
* @param reference_out pointer to output reference or NULL
* @param repo the repository to search in
* @param spec the textual specification for an object
* @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC
* or an error code
*/
GIT_EXTERN(int) git_revparse_ext(
git_object **object_out,
git_reference **reference_out,
git_repository *repo,
const char *spec);
/**
* Revparse flags. These indicate the intended behavior of the spec passed to
......
......@@ -84,12 +84,16 @@ static int maybe_describe(git_object**out, git_repository *repo, const char *spe
return maybe_abbrev(out, repo, substr+2);
}
static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
static int revparse_lookup_object(
git_object **object_out,
git_reference **reference_out,
git_repository *repo,
const char *spec)
{
int error;
git_reference *ref;
error = maybe_sha(out, repo, spec);
error = maybe_sha(object_out, repo, spec);
if (!error)
return 0;
......@@ -98,22 +102,27 @@ static int revparse_lookup_object(git_object **out, git_repository *repo, const
error = git_reference_dwim(&ref, repo, spec);
if (!error) {
error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY);
git_reference_free(ref);
error = git_object_lookup(
object_out, repo, git_reference_target(ref), GIT_OBJ_ANY);
if (!error)
*reference_out = ref;
return error;
}
if (error < 0 && error != GIT_ENOTFOUND)
return error;
error = maybe_abbrev(out, repo, spec);
error = maybe_abbrev(object_out, repo, spec);
if (!error)
return 0;
if (error < 0 && error != GIT_ENOTFOUND)
return error;
error = maybe_describe(out, repo, spec);
error = maybe_describe(object_out, repo, spec);
if (!error)
return 0;
......@@ -617,14 +626,8 @@ static int ensure_base_rev_loaded(git_object **object, git_reference **reference
if (*object != NULL)
return 0;
if (*reference != NULL) {
if ((error = object_from_reference(object, *reference)) < 0)
return error;
git_reference_free(*reference);
*reference = NULL;
return 0;
}
if (*reference != NULL)
return object_from_reference(object, *reference);
if (!allow_empty_identifier && identifier_len == 0)
return GIT_EINVALIDSPEC;
......@@ -632,7 +635,7 @@ static int ensure_base_rev_loaded(git_object **object, git_reference **reference
if (git_buf_put(&identifier, spec, identifier_len) < 0)
return -1;
error = revparse_lookup_object(object, repo, git_buf_cstr(&identifier));
error = revparse_lookup_object(object, reference, repo, git_buf_cstr(&identifier));
git_buf_free(&identifier);
return error;
......@@ -668,7 +671,12 @@ static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_
return GIT_EINVALIDSPEC;
}
int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
int revparse__ext(
git_object **object_out,
git_reference **reference_out,
int *identifier_len_out,
git_repository *repo,
const char *spec)
{
size_t pos = 0, identifier_len = 0;
int error = -1, n;
......@@ -677,9 +685,10 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
git_reference *reference = NULL;
git_object *base_rev = NULL;
assert(out && repo && spec);
assert(object_out && reference_out && repo && spec);
*out = NULL;
*object_out = NULL;
*reference_out = NULL;
while (spec[pos]) {
switch (spec[pos]) {
......@@ -798,7 +807,9 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
goto cleanup;
*out = base_rev;
*object_out = base_rev;
*reference_out = reference;
*identifier_len_out = identifier_len;
error = 0;
cleanup:
......@@ -808,12 +819,59 @@ cleanup:
"Failed to parse revision specifier - Invalid pattern '%s'", spec);
git_object_free(base_rev);
}
git_reference_free(reference);
}
git_buf_free(&buf);
return error;
}
int git_revparse_ext(
git_object **object_out,
git_reference **reference_out,
git_repository *repo,
const char *spec)
{
int error, identifier_len;
git_object *obj = NULL;
git_reference *ref = NULL;
if ((error = revparse__ext(&obj, &ref, &identifier_len, repo, spec)) < 0)
goto cleanup;
*object_out = obj;
*reference_out = ref;
return 0;
cleanup:
git_object_free(obj);
git_reference_free(ref);
return error;
}
int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
{
int error;
git_object *obj = NULL;
git_reference *ref = NULL;
*out = NULL;
if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0)
goto cleanup;
git_reference_free(ref);
*out = obj;
return 0;
cleanup:
git_object_free(obj);
git_reference_free(ref);
return error;
}
int git_revparse(
git_revspec *revspec,
......
......@@ -9,13 +9,19 @@ static git_repository *g_repo;
static git_object *g_obj;
/* Helpers */
static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
static void test_object_and_ref_inrepo(
const char *spec,
const char *expected_oid,
const char *expected_refname,
git_repository *repo,
bool assert_reference_retrieval)
{
char objstr[64] = {0};
git_object *obj = NULL;
git_reference *ref = NULL;
int error;
error = git_revparse_single(&obj, repo, spec);
error = git_revparse_ext(&obj, &ref, repo, spec);
if (expected_oid != NULL) {
cl_assert_equal_i(0, error);
......@@ -24,7 +30,20 @@ static void test_object_inrepo(const char *spec, const char *expected_oid, git_r
} else
cl_assert_equal_i(GIT_ENOTFOUND, error);
if (assert_reference_retrieval) {
if (expected_refname == NULL)
cl_assert(NULL == ref);
else
cl_assert_equal_s(expected_refname, git_reference_name(ref));
}
git_object_free(obj);
git_reference_free(ref);
}
static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
{
test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false);
}
static void test_id_inrepo(
......@@ -63,6 +82,11 @@ static void test_object(const char *spec, const char *expected_oid)
test_object_inrepo(spec, expected_oid, g_repo);
}
static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname)
{
test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true);
}
static void test_rangelike(const char *rangelike,
const char *expected_left,
const char *expected_right,
......@@ -694,3 +718,24 @@ void test_refs_revparse__parses_range_operator(void)
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
}
void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
{
test_object_and_ref(
"master@{upstream}",
"be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
"refs/remotes/test/master");
test_object_and_ref(
"@{-1}",
"a4a7dce85cf63874e984719f4fdd239f5145052f",
"refs/heads/br2");
}
void test_refs_revparse__ext_can_expand_short_reference_names(void)
{
test_object_and_ref(
"master",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
"refs/heads/master");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment