Commit ac250c56 by Ben Straub

First stab at implementation of rev-parse.

This version supports refspecs of these kinds:
- Full & partial SHAs
- Output from "git describe"
- "/refs/heads/master" (full ref names)
- "master" (partial ref names)
- "FETCH_HEAD" (named heads)
parent fb49bdf9
/*
* Copyright (C) 2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_revparse_h__
#define INCLUDE_git_revparse_h__
#include "common.h"
#include "types.h"
GIT_BEGIN_DECL
GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
//GIT_EXTERN(int) git_revparse_multi(TODO);
GIT_END_DECL
#endif
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <assert.h>
#include "common.h"
#include "buffer.h"
#include "git2/revparse.h"
#include "git2/object.h"
#include "git2/oid.h"
#include "git2/refs.h"
GIT_BEGIN_DECL
typedef enum {
REVPARSE_STATE_INIT,
/* for parsing "@{...}" */
REVPARSE_STATE_REF_A,
REVPARSE_STATE_REF_B,
/* for "^{...}" and ^... */
REVPARSE_STATE_PARENTS_A,
REVPARSE_STATE_PARENTS_B,
/* For "~..." */
REVPARSE_STATE_LIINEAR,
/* For joining parents and linear, as in "master^2~3^2" */
REVPARSE_STATE_JOIN,
} revparse_state;
static int revparse_lookup_fully_qualifed_ref(git_object **out, git_repository *repo, const char*spec)
{
git_reference *ref;
git_object *obj = NULL;
if (!git_reference_lookup(&ref, repo, spec)) {
git_reference *resolved_ref;
if (!git_reference_resolve(&resolved_ref, ref)) {
if (!git_object_lookup(&obj, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY)) {
*out = obj;
}
git_reference_free(resolved_ref);
}
git_reference_free(ref);
}
if (obj) {
return 0;
}
return GIT_ERROR;
}
static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
{
size_t speclen = strlen(spec);
git_object *obj = NULL;
git_oid oid;
git_buf refnamebuf = GIT_BUF_INIT;
static const char* formatters[] = {
"refs/%s",
"refs/tags/%s",
"refs/heads/%s",
"refs/remotes/%s",
"refs/remotes/%s/HEAD",
NULL
};
unsigned int i;
const char *substr;
/* "git describe" output; snip everything before/including "-g" */
substr = strstr(spec, "-g");
if (substr) {
spec = substr + 2;
speclen = strlen(spec);
}
/* SHA or prefix */
if (!git_oid_fromstrn(&oid, spec, speclen)) {
if (!git_object_lookup_prefix(&obj, repo, &oid, speclen, GIT_OBJ_ANY)) {
*out = obj;
return 0;
}
}
/* Fully-named ref */
if (!revparse_lookup_fully_qualifed_ref(&obj, repo, spec)) {
*out = obj;
return 0;
}
/* Partially-named ref; match in this order: */
for (i=0; formatters[i]; i++) {
git_buf_clear(&refnamebuf);
if (git_buf_printf(&refnamebuf, formatters[i], spec) < 0) {
return GIT_ERROR;
}
if (!revparse_lookup_fully_qualifed_ref(&obj, repo, git_buf_cstr(&refnamebuf))) {
git_buf_free(&refnamebuf);
*out = obj;
return 0;
}
}
git_buf_free(&refnamebuf);
giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
return GIT_ERROR;
}
static void set_invalid_syntax_err(const char *spec)
{
giterr_set(GITERR_INVALID, "Refspec '%s' is not valid.", spec);
}
int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
{
revparse_state current_state = REVPARSE_STATE_INIT;
revparse_state next_state = REVPARSE_STATE_INIT;
const char *spec_cur = spec;
git_object *obj = NULL;
git_buf specbuffer = GIT_BUF_INIT;
git_buf stepbuffer = GIT_BUF_INIT;
assert(out && repo && spec);
while (1) {
switch (current_state) {
case REVPARSE_STATE_INIT:
if (!*spec_cur) {
/* No operators, just a name. Find it and return. */
return revparse_lookup_object(out, repo, spec);
} else if (*spec_cur == '@') {
next_state = REVPARSE_STATE_REF_A;
}
spec_cur++;
if (current_state != next_state) {
/* Leaving INIT state, find the object specified and carry on */
assert(!git_buf_set(&specbuffer, spec, spec_cur - spec));
assert(!revparse_lookup_object(&obj, repo, git_buf_cstr(&specbuffer)));
}
break;
case REVPARSE_STATE_REF_A:
/* Found '@', look for '{', fail otherwise */
if (*spec_cur != '{') {
set_invalid_syntax_err(spec);
return GIT_ERROR;
}
spec_cur++;
next_state = REVPARSE_STATE_REF_B;
break;
case REVPARSE_STATE_REF_B:
/* Found "@{", gather things until a '}' */
break;
}
current_state = next_state;
}
return 0;
}
GIT_END_DECL
#include "clar_libgit2.h"
#include "git2/revparse.h"
static git_repository *g_repo;
static git_object *g_obj;
// Hepers
static void oid_str_cmp(const git_oid *oid, const char *str)
{
git_oid oid2;
cl_git_pass(git_oid_fromstr(&oid2, str));
cl_assert(0 == git_oid_cmp(oid, &oid2));
}
void test_refs_revparse__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo.git");
}
void test_refs_revparse__cleanup(void)
{
cl_git_sandbox_cleanup();
g_obj = NULL;
}
void test_refs_revparse__shas(void)
{
// Full SHA should return a valid object
cl_git_pass(git_revparse_single(&g_obj, g_repo, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
oid_str_cmp(git_object_id(g_obj), "c47800c7266a2be04c571c04d5a6614691ea99bd");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "c47800c"));
oid_str_cmp(git_object_id(g_obj), "c47800c7266a2be04c571c04d5a6614691ea99bd");
}
void test_refs_revparse__head(void)
{
// Named head should return a valid object
cl_git_pass(git_revparse_single(&g_obj, g_repo, "HEAD"));
oid_str_cmp(git_object_id(g_obj), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__full_refs(void)
{
// Fully-qualified refs should return valid objects
cl_git_pass(git_revparse_single(&g_obj, g_repo, "refs/heads/master"));
oid_str_cmp(git_object_id(g_obj), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "refs/heads/test"));
oid_str_cmp(git_object_id(g_obj), "e90810b8df3e80c413d903f631643c716887138d");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "refs/tags/test"));
oid_str_cmp(git_object_id(g_obj), "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
}
void test_refs_revparse__partial_refs(void)
{
// Partially-qualified refs should return valid objects
cl_git_pass(git_revparse_single(&g_obj, g_repo, "point_to_blob"));
oid_str_cmp(git_object_id(g_obj), "1385f264afb75a56a5bec74243be9b367ba4ca08");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "packed-test"));
oid_str_cmp(git_object_id(g_obj), "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "br2"));
oid_str_cmp(git_object_id(g_obj), "a4a7dce85cf63874e984719f4fdd239f5145052f");
}
void test_refs_revparse__describe_output(void)
{
cl_git_pass(git_revparse_single(&g_obj, g_repo, "blah-7-gc47800c"));
oid_str_cmp(git_object_id(g_obj), "c47800c7266a2be04c571c04d5a6614691ea99bd");
}
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