Implemented partial caret syntax for rev-parse. Supported forms: - "^n" - "^0" - "^" Still missing: all of the "^{…}" variants.
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
diff --git a/src/revparse.c b/src/revparse.c
index f9213d9..3a1cd05 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -14,6 +14,8 @@
#include "git2/object.h"
#include "git2/oid.h"
#include "git2/refs.h"
+#include "git2/tag.h"
+#include "git2/commit.h"
GIT_BEGIN_DECL
@@ -146,7 +148,14 @@ static git_object* dereference_object(git_object *obj)
break;
case GIT_OBJ_REF_DELTA:
break;
- }}
+
+ default:
+ break;
+ }
+
+ /* Can't dereference some types */
+ return NULL;
+}
static int dereference_to_type(git_object **out, git_object *obj, git_otype target_type)
{
@@ -158,6 +167,11 @@ static int dereference_to_type(git_object **out, git_object *obj, git_otype targ
return 0;
}
+ if (this_type == GIT_OBJ_TAG) {
+ git_tag_peel(&obj, (git_tag*)obj);
+ continue;
+ }
+
/* Dereference once, if possible. */
obj = dereference_object(obj);
@@ -167,8 +181,8 @@ static int dereference_to_type(git_object **out, git_object *obj, git_otype targ
static int handle_caret_syntax(git_object **out, git_object *start, const char *movement)
{
git_object *obj;
-
- printf("Moving by '%s'\n", movement);
+ git_commit *commit;
+ int n;
if (*movement == '{') {
if (movement[strlen(movement)-1] != '}') {
@@ -184,10 +198,29 @@ static int handle_caret_syntax(git_object **out, git_object *start, const char *
/* Dereference until we reach a commit. */
if (dereference_to_type(&obj, start, GIT_OBJ_COMMIT) < 0) {
+ /* Can't dereference to a commit; fail */
+ return GIT_ERROR;
}
- /* Move to the Nth parent. */
+ /* "^" is the same as "^1" */
+ if (strlen(movement) == 0) {
+ n = 1;
+ } else {
+ git__strtol32(&n, movement, NULL, 0);
+ }
+ commit = (git_commit*)obj;
+
+ /* "^0" just returns the input */
+ if (n == 0) {
+ *out = (git_object*)commit;
+ return 0;
+ }
+ if (git_commit_parent(&commit, commit, n-1) < 0) {
+ return GIT_ERROR;
+ }
+
+ *out = (git_object*)commit;
return 0;
}
@@ -197,6 +230,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
revparse_state next_state = REVPARSE_STATE_INIT;
const char *spec_cur = spec;
git_object *cur_obj = NULL;
+ git_object *next_obj = NULL;
git_buf specbuffer = GIT_BUF_INIT;
git_buf stepbuffer = GIT_BUF_INIT;
int retcode = 0;
@@ -210,6 +244,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
/* No operators, just a name. Find it and return. */
return revparse_lookup_object(out, repo, spec);
} else if (*spec_cur == '@') {
+ /* '@' syntax doesn't allow chaining */
git_buf_puts(&stepbuffer, spec_cur);
retcode = walk_ref_history(out, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer));
goto cleanup;
@@ -224,7 +259,7 @@ 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(&cur_obj, repo, git_buf_cstr(&specbuffer)));
+ assert(!revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer)));
}
break;
@@ -237,15 +272,17 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
git_buf_cstr(&stepbuffer));
goto cleanup;
} else if (*spec_cur == '~') {
- retcode = handle_caret_syntax(&cur_obj,
+ retcode = handle_caret_syntax(&next_obj,
cur_obj,
git_buf_cstr(&stepbuffer));
+ git_buf_clear(&stepbuffer);
if (retcode < 0) goto cleanup;
next_state = REVPARSE_STATE_LINEAR;
} else if (*spec_cur == '^') {
- retcode = handle_caret_syntax(&cur_obj,
+ retcode = handle_caret_syntax(&next_obj,
cur_obj,
git_buf_cstr(&stepbuffer));
+ git_buf_clear(&stepbuffer);
if (retcode < 0) goto cleanup;
next_state = REVPARSE_STATE_CARET;
} else {
@@ -259,6 +296,10 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec
}
current_state = next_state;
+ if (cur_obj != next_obj) {
+ git_object_free(cur_obj);
+ cur_obj = next_obj;
+ }
}
cleanup:
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 90aaf54..9ff3bd1 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -76,12 +76,16 @@ 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");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^"));
+ oid_str_cmp(git_object_id(g_obj), "9fd738e8f7967c078dceed8190330fc8648ee56a");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^2"));
oid_str_cmp(git_object_id(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");
cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^2^1"));
oid_str_cmp(git_object_id(g_obj), "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ cl_git_pass(git_revparse_single(&g_obj, g_repo, "be3563a^0"));
+ oid_str_cmp(git_object_id(g_obj), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
}
void test_refs_revparse__reflog(void)