Commit db9be945 by nulltoken

object: introduce git_object_peel()

Partially fix #530
parent b8748c12
......@@ -167,6 +167,23 @@ GIT_EXTERN(int) git_object_typeisloose(git_otype type);
*/
GIT_EXTERN(size_t) git_object__size(git_otype type);
/**
* Recursively peel an object until an object of the specified
* type is met
*
* The retrieved `peeled` object is owned by the repository
* and should be closed with the `git_object_free` method.
*
* @param peeled Pointer to the peeled git_object
* @param object The object to be processed
* @param target_type The type of the requested object
* @return 0 or an error code
*/
GIT_EXTERN(int) git_object_peel(
git_object **peeled,
git_object *object,
git_otype target_type);
/** @} */
GIT_END_DECL
......
......@@ -333,3 +333,72 @@ int git_object__resolve_to_type(git_object **obj, git_otype type)
*obj = scan;
return error;
}
static int dereference_object(git_object **dereferenced, git_object *obj)
{
git_otype type = git_object_type(obj);
switch (type) {
case GIT_OBJ_COMMIT:
return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
break;
case GIT_OBJ_TAG:
return git_tag_target(dereferenced, (git_tag*)obj);
break;
default:
return GIT_ENOTFOUND;
break;
}
}
static int peel_error(int error, const char* msg)
{
giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg);
return error;
}
int git_object_peel(
git_object **peeled,
git_object *object,
git_otype target_type)
{
git_object *source, *deref = NULL;
assert(object);
if (git_object_type(object) == target_type)
return git_object__dup(peeled, object);
if (target_type == GIT_OBJ_BLOB
|| target_type == GIT_OBJ_ANY)
return peel_error(GIT_EAMBIGUOUS, "Ambiguous target type");
if (git_object_type(object) == GIT_OBJ_BLOB)
return peel_error(GIT_ERROR, "A blob cannot be dereferenced");
source = object;
while (true) {
if (dereference_object(&deref, source) < 0)
goto cleanup;
if (source != object)
git_object_free(source);
if (git_object_type(deref) == target_type) {
*peeled = deref;
return 0;
}
source = deref;
deref = NULL;
}
cleanup:
if (source != object)
git_object_free(source);
git_object_free(deref);
return -1;
}
#include "clar_libgit2.h"
static git_repository *g_repo;
void test_object_peel__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
}
void test_object_peel__cleanup(void)
{
git_repository_free(g_repo);
}
static void assert_peel(const char* expected_sha, const char *sha, git_otype requested_type)
{
git_oid oid, expected_oid;
git_object *obj;
git_object *peeled;
cl_git_pass(git_oid_fromstr(&oid, sha));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_pass(git_object_peel(&peeled, obj, requested_type));
cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
git_object_free(peeled);
git_object_free(obj);
}
static void assert_peel_error(int error, const char *sha, git_otype requested_type)
{
git_oid oid;
git_object *obj;
git_object *peeled;
cl_git_pass(git_oid_fromstr(&oid, sha));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_assert_equal_i(error, git_object_peel(&peeled, obj, requested_type));
git_object_free(obj);
}
void test_object_peel__peeling_an_object_into_its_own_type_returns_another_instance_of_it(void)
{
assert_peel("e90810b8df3e80c413d903f631643c716887138d", "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TAG);
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB);
}
void test_object_peel__can_peel_a_tag(void)
{
assert_peel("e90810b8df3e80c413d903f631643c716887138d", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_COMMIT);
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TREE);
}
void test_object_peel__can_peel_a_commit(void)
{
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_TREE);
}
void test_object_peel__cannot_peel_a_tree(void)
{
assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB);
}
void test_object_peel__cannot_peel_a_blob(void)
{
assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT);
}
void test_object_peel__cannot_target_any_object(void)
{
assert_peel_error(GIT_EAMBIGUOUS, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY);
}
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