summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Straub <bstraub@github.com>2012-04-25 16:24:22 -0700
committerBen Straub <bstraub@github.com>2012-05-11 11:30:45 -0700
commitac250c56c7d7bb11691c9dfbcd0dbf580d85e177 (patch)
tree09684c246208dcf49386561203b6bc2e255ba532
parentfb49bdf9c7837892154bf7efdb3db6c3ec63e396 (diff)
downloadlibgit2-ac250c56c7d7bb11691c9dfbcd0dbf580d85e177.tar.gz
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)
-rw-r--r--include/git2/revparse.h22
-rw-r--r--src/revparse.c175
-rw-r--r--tests-clar/refs/revparse.c74
3 files changed, 271 insertions, 0 deletions
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
new file mode 100644
index 000000000..3fd69d91d
--- /dev/null
+++ b/include/git2/revparse.h
@@ -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
diff --git a/src/revparse.c b/src/revparse.c
new file mode 100644
index 000000000..d6a7ebd19
--- /dev/null
+++ b/src/revparse.c
@@ -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
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
new file mode 100644
index 000000000..05c12b074
--- /dev/null
+++ b/tests-clar/refs/revparse.c
@@ -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");
+}
+