Commit 9d7bdf7119fe7858fdaa6e79283dacbf98c4128c

Ben Straub 2012-04-26T18:15:43

Implemented rev-parse's "^{}" syntax.

diff --git a/src/revparse.c b/src/revparse.c
index 3a1cd05..da274f8 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -143,6 +143,9 @@ static git_object* dereference_object(git_object *obj)
    case GIT_OBJ_BLOB:
       break;
    case GIT_OBJ_TAG:
+      if (0 == git_tag_target(&newobj, (git_tag*)obj)) {
+         return newobj;
+      }
       break;
    case GIT_OBJ_OFS_DELTA:
       break;
@@ -159,28 +162,36 @@ static git_object* dereference_object(git_object *obj)
 
 static int dereference_to_type(git_object **out, git_object *obj, git_otype target_type)
 {
-   git_otype this_type = git_object_type(obj);
+   git_object *obj1 = obj, *obj2 = obj;
 
    while (1) {
+      git_otype this_type = git_object_type(obj1);
+
       if (this_type == target_type) {
          *out = obj;
          return 0;
       }
 
-      if (this_type == GIT_OBJ_TAG) {
-         git_tag_peel(&obj, (git_tag*)obj);
-         continue;
-      }
-
       /* Dereference once, if possible. */
-      obj = dereference_object(obj);
-
+      obj2 = dereference_object(obj1);
+      if (obj2 != obj) {
+         git_object_free(obj2);
+      }
+      obj1 = obj2;
    }
 }
 
-static int handle_caret_syntax(git_object **out, git_object *start, const char *movement)
+static git_otype parse_obj_type(const char *str)
+{
+   if (!strcmp(str, "{commit}")) return GIT_OBJ_COMMIT;
+   if (!strcmp(str, "{tree}")) return GIT_OBJ_TREE;
+   if (!strcmp(str, "{blob}")) return GIT_OBJ_BLOB;
+   if (!strcmp(str, "{tag}")) return GIT_OBJ_TAG;
+   return GIT_OBJ_BAD;
+}
+
+static int handle_caret_syntax(git_object **out, git_object *obj, const char *movement)
 {
-   git_object *obj;
    git_commit *commit;
    int n;
 
@@ -189,15 +200,40 @@ static int handle_caret_syntax(git_object **out, git_object *start, const char *
          set_invalid_syntax_err(movement);
          return GIT_ERROR;
       }
+
+      /* {} -> Dereference until we reach an object that isn't a tag. */
+      if (strlen(movement) == 2) {
+         git_object *newobj = obj;
+         git_object *newobj2 = newobj;
+         while (git_object_type(newobj2) == GIT_OBJ_TAG) {
+            newobj2 = dereference_object(newobj);
+            if (newobj != obj) git_object_free(newobj);
+            if (!newobj2) {
+               giterr_set(GITERR_REFERENCE, "Couldn't find object of target type.");
+               return GIT_ERROR;
+            }
+            newobj = newobj;
+         }
+         *out = newobj2;
+         return 0;
+      }
       
-      // TODO
       /* {/...} -> Walk all commits until we see a commit msg that matches the phrase. */
-      /* {} -> Dereference until we reach an object that isn't a tag. */
+      if (movement[1] == '/') {
+         // TODO
+         return GIT_ERROR;
+      }
+
       /* {...} -> Dereference until we reach an object of a certain type. */
+      if (dereference_to_type(out, obj, parse_obj_type(movement)) < 0) {
+         giterr_set(GITERR_REFERENCE, "Can't dereference to type");
+         return GIT_ERROR;
+      }
+      return 0;
    }
 
    /* Dereference until we reach a commit. */
-   if (dereference_to_type(&obj, start, GIT_OBJ_COMMIT) < 0) {
+   if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) {
       /* Can't dereference to a commit; fail */
       return GIT_ERROR;
    }
@@ -212,7 +248,7 @@ static int handle_caret_syntax(git_object **out, git_object *start, const char *
 
    /* "^0" just returns the input */
    if (n == 0) {
-      *out = (git_object*)commit;
+      *out = obj;
       return 0;
    }
 
@@ -259,7 +295,10 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
 
          if (current_state != next_state) {
             /* Leaving INIT state, find the object specified, in case that state needs it */
-            assert(!revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer)));
+            retcode = revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer));
+            if (retcode < 0) {
+               goto cleanup;
+            }
          }
          break;
 
@@ -292,6 +331,13 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
          break;
 
       case REVPARSE_STATE_LINEAR:
+         if (!*spec_cur) {
+         } else if (*spec_cur == '~') {
+         } else if (*spec_cur == '^') {
+         } else {
+            git_buf_putc(&stepbuffer, *spec_cur);
+         }
+         spec_cur++;
          break;
       }
 
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 9ff3bd1..64aca4a 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -8,11 +8,11 @@ static git_object *g_obj;
 
 
 // Hepers
-static void oid_str_cmp(const git_oid *oid, const char *str)
+static void oid_str_cmp(const git_object *obj, const char *expected)
 {
-   git_oid oid2;
-   cl_git_pass(git_oid_fromstr(&oid2, str));
-   cl_assert(0 == git_oid_cmp(oid, &oid2));
+   char objstr[64] = {0};
+   git_oid_to_string(objstr, 64, git_object_id(obj));
+   cl_assert_equal_s(objstr, expected);
 }
 
 
@@ -28,64 +28,81 @@ void test_refs_revparse__cleanup(void)
 }
 
 
+void test_refs_revparse__nonexistant_object(void)
+{
+   cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't exist"));
+}
+
 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");
+   oid_str_cmp(g_obj, "c47800c7266a2be04c571c04d5a6614691ea99bd");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "c47800c"));
-   oid_str_cmp(git_object_id(g_obj), "c47800c7266a2be04c571c04d5a6614691ea99bd");
+   oid_str_cmp(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");
+   oid_str_cmp(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");
+   oid_str_cmp(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");
+   oid_str_cmp(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");
+   oid_str_cmp(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");
+   oid_str_cmp(g_obj, "1385f264afb75a56a5bec74243be9b367ba4ca08");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "packed-test"));
-   oid_str_cmp(git_object_id(g_obj), "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+   oid_str_cmp(g_obj, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "br2"));
-   oid_str_cmp(git_object_id(g_obj), "a4a7dce85cf63874e984719f4fdd239f5145052f");
+   oid_str_cmp(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");
+   oid_str_cmp(g_obj, "c47800c7266a2be04c571c04d5a6614691ea99bd");
 }
 
 void test_refs_revparse__nth_parent(void)
 {
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^1"));
-   oid_str_cmp(git_object_id(g_obj), "9fd738e8f7967c078dceed8190330fc8648ee56a");
+   oid_str_cmp(g_obj, "9fd738e8f7967c078dceed8190330fc8648ee56a");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^"));
-   oid_str_cmp(git_object_id(g_obj), "9fd738e8f7967c078dceed8190330fc8648ee56a");
+   oid_str_cmp(g_obj, "9fd738e8f7967c078dceed8190330fc8648ee56a");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^2"));
-   oid_str_cmp(git_object_id(g_obj), "c47800c7266a2be04c571c04d5a6614691ea99bd");
+   oid_str_cmp(g_obj, "c47800c7266a2be04c571c04d5a6614691ea99bd");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^1^1"));
-   oid_str_cmp(git_object_id(g_obj), "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+   oid_str_cmp(g_obj, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^2^1"));
-   oid_str_cmp(git_object_id(g_obj), "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+   oid_str_cmp(g_obj, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
    cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^0"));
-   oid_str_cmp(git_object_id(g_obj), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+   oid_str_cmp(g_obj, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__not_tag(void)
+{
+   cl_git_pass(git_revparse_single(&g_obj, g_repo, "point_to_blob^{}"));
+   oid_str_cmp(g_obj, "1385f264afb75a56a5bec74243be9b367ba4ca08");
+   cl_git_pass(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{}"));
+   oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__to_type(void)
+{
 }
 
 void test_refs_revparse__reflog(void)
diff --git a/tests/resources/testrepo.git/objects/d7/eddec7b3610726791785bf839065953f10341b b/tests/resources/testrepo.git/objects/d7/eddec7b3610726791785bf839065953f10341b
new file mode 100644
index 0000000..32fc532
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d7/eddec7b3610726791785bf839065953f10341b
@@ -0,0 +1,2 @@
+xM F]s41x(IKݽ/_P@!8)es
+	N&FGSƄh{+CZzvF7Z-kx\[PX<:m쥗RG./
\ No newline at end of file
diff --git a/tests/resources/testrepo.git/refs/tags/hard_tag b/tests/resources/testrepo.git/refs/tags/hard_tag
new file mode 100644
index 0000000..1621c20
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/hard_tag
@@ -0,0 +1 @@
+d7eddec7b3610726791785bf839065953f10341b
diff --git a/tests/resources/testrepo.git/refs/tags/wrapped_tag b/tests/resources/testrepo.git/refs/tags/wrapped_tag
new file mode 100644
index 0000000..1621c20
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/wrapped_tag
@@ -0,0 +1 @@
+d7eddec7b3610726791785bf839065953f10341b