Rev-parse: add "tag:README" syntax.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
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");
+}