Commit 244d2f6b80161978cce5bacc3ac6e663b530bb65

Ben Straub 2012-05-30T16:52:11

Rev-parse: add "tag:README" syntax.

diff --git a/src/revparse.c b/src/revparse.c
index 3615ac5..1e78d76 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -10,6 +10,7 @@
 #include "common.h"
 #include "buffer.h"
 #include "date.h"
+#include "tree.h"
 
 #include "git2.h"
 
@@ -19,6 +20,7 @@ typedef enum {
    REVPARSE_STATE_INIT,
    REVPARSE_STATE_CARET,
    REVPARSE_STATE_LINEAR,
+   REVPARSE_STATE_COLON,
    REVPARSE_STATE_DONE,
 } revparse_state;
 
@@ -535,6 +537,45 @@ static int handle_linear_syntax(git_object **out, git_object *obj, const char *m
    return 0;
 }
 
+static const git_tree_entry* git_tree_entry_bypath(git_tree *tree, git_repository *repo, const char *path)
+{
+   char *str = git__strdup(path);
+   char *tok;
+   git_tree *tree2 = tree;
+   const git_tree_entry *entry;
+
+   while ((tok = git__strtok(&str, "/\\")) != NULL) {
+      entry = git_tree_entry_byname(tree2, tok);
+      if (tree2 != tree) git_tree_free(tree2);
+      if (entry_is_tree(entry)) {
+         if (git_tree_lookup(&tree2, repo, &entry->oid) < 0) {
+            return NULL;
+         }
+      }
+   }
+
+   return entry;
+}
+
+static int handle_colon_syntax(git_object **out,
+                               git_repository *repo,
+                               git_object *obj,
+                               const char *path)
+{
+   git_tree *tree;
+   const git_tree_entry *entry;
+
+   /* Dereference until we reach a tree. */
+   if (dereference_to_type(&obj, obj, GIT_OBJ_TREE) < 0) {
+      return GIT_ERROR;
+   }
+   tree = (git_tree*)obj;
+
+   /* Find the blob at the given path. */
+   entry = git_tree_entry_bypath(tree, repo, path);
+   return git_tree_entry_2object(out, repo, entry);
+}
+
 int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
 {
    revparse_state current_state = REVPARSE_STATE_INIT,  next_state = REVPARSE_STATE_INIT;
@@ -545,6 +586,13 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
 
    assert(out && repo && spec);
 
+   if (spec[0] == ':') {
+      /* Either a global grep (":/foo") or a merge-stage path lookup (":2:Makefile").
+         Neither of these are handled just yet. */
+      giterr_set(GITERR_INVALID, "Unimplemented");
+      return GIT_ERROR;
+   }
+
    while (current_state != REVPARSE_STATE_DONE) {
       switch (current_state) {
       case REVPARSE_STATE_INIT:
@@ -561,6 +609,8 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
             next_state = REVPARSE_STATE_CARET;
          } else if (*spec_cur == '~') {
             next_state = REVPARSE_STATE_LINEAR;
+         } else if (*spec_cur == ':') {
+            next_state = REVPARSE_STATE_COLON;
          } else {
             git_buf_putc(&specbuffer, *spec_cur);
          }
@@ -617,6 +667,16 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
          spec_cur++;
          break;
 
+      case REVPARSE_STATE_COLON:
+         if (*spec_cur) {
+            git_buf_putc(&stepbuffer, *spec_cur);
+         } else {
+            retcode = handle_colon_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer));
+            next_state = REVPARSE_STATE_DONE;
+         }
+         spec_cur++;
+         break;
+
       case REVPARSE_STATE_DONE:
          if (cur_obj && *out != cur_obj) git_object_free(cur_obj);
          if (next_obj && *out != next_obj) git_object_free(next_obj);
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 783b03b..2da8574 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -153,3 +153,13 @@ void test_refs_revparse__date(void)
    /* Core git gives a65fedf, because they don't take time zones into account. */
    test_object("master@{1335806640}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
 }
+
+void test_refs_revparse__colon(void)
+{
+   cl_git_fail(git_revparse_single(&g_obj, g_repo, ":/foo"));
+   cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README"));
+
+   test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
+   test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+   test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+}