mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-23 13:00:02 +00:00
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)
This commit is contained in:
parent
fb49bdf9c7
commit
ac250c56c7
22
include/git2/revparse.h
Normal file
22
include/git2/revparse.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
||||
175
src/revparse.c
Normal file
175
src/revparse.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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
|
||||
74
tests-clar/refs/revparse.c
Normal file
74
tests-clar/refs/revparse.c
Normal file
@ -0,0 +1,74 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user