From 023c6f69edcacfefe36a2b7d606def32d5bc246c Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Wed, 25 Apr 2012 19:08:17 -0700 Subject: [PATCH] Simpler states and initial structure. New tests for "foo^2" syntax, but they don't pass yet. Support for chaining these, i.e. "foo^2~3^{u}~1' is starting to shape up. --- src/revparse.c | 150 ++++++++++++++++++++++++++++++------- tests-clar/refs/revparse.c | 16 ++++ 2 files changed, 139 insertions(+), 27 deletions(-) diff --git a/src/revparse.c b/src/revparse.c index d6a7ebd19..f9213d9be 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -20,21 +20,18 @@ 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, + REVPARSE_STATE_CARET, /* For "~..." */ - REVPARSE_STATE_LIINEAR, - - /* For joining parents and linear, as in "master^2~3^2" */ - REVPARSE_STATE_JOIN, + REVPARSE_STATE_LINEAR, } revparse_state; +static void set_invalid_syntax_err(const char *spec) +{ + giterr_set(GITERR_INVALID, "Refspec '%s' is not valid.", spec); +} + static int revparse_lookup_fully_qualifed_ref(git_object **out, git_repository *repo, const char*spec) { git_reference *ref; @@ -115,20 +112,94 @@ static int revparse_lookup_object(git_object **out, git_repository *repo, const } -static void set_invalid_syntax_err(const char *spec) +static int walk_ref_history(git_object **out, const char *refspec, const char *reflogspec) { - giterr_set(GITERR_INVALID, "Refspec '%s' is not valid.", spec); + // TODO + + /* Empty refspec means current branch */ + + /* Possible syntaxes for reflogspec: + * "8" -> 8th prior value for the ref + * "-2" -> nth branch checked out before the current one (refspec must be empty) + * "yesterday", "1 month 2 weeks 3 days 4 hours 5 seconds ago", "1979-02-26 18:30:00" + * -> value of ref at given point in time + * "upstream" or "u" -> branch the ref is set to build on top of + * */ + return 0; } +static git_object* dereference_object(git_object *obj) +{ + git_otype type = git_object_type(obj); + git_object *newobj = NULL; + + switch (type) { + case GIT_OBJ_COMMIT: + break; + case GIT_OBJ_TREE: + break; + case GIT_OBJ_BLOB: + break; + case GIT_OBJ_TAG: + break; + case GIT_OBJ_OFS_DELTA: + break; + case GIT_OBJ_REF_DELTA: + break; + }} + +static int dereference_to_type(git_object **out, git_object *obj, git_otype target_type) +{ + git_otype this_type = git_object_type(obj); + + while (1) { + if (this_type == target_type) { + *out = obj; + return 0; + } + + /* Dereference once, if possible. */ + obj = dereference_object(obj); + + } +} + +static int handle_caret_syntax(git_object **out, git_object *start, const char *movement) +{ + git_object *obj; + + printf("Moving by '%s'\n", movement); + + if (*movement == '{') { + if (movement[strlen(movement)-1] != '}') { + set_invalid_syntax_err(movement); + return GIT_ERROR; + } + + // 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. */ + /* {...} -> Dereference until we reach an object of a certain type. */ + } + + /* Dereference until we reach a commit. */ + if (dereference_to_type(&obj, start, GIT_OBJ_COMMIT) < 0) { + } + + /* Move to the Nth parent. */ + + return 0; +} 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_object *cur_obj = NULL; git_buf specbuffer = GIT_BUF_INIT; git_buf stepbuffer = GIT_BUF_INIT; + int retcode = 0; assert(out && repo && spec); @@ -139,36 +210,61 @@ 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 == '@') { - next_state = REVPARSE_STATE_REF_A; + git_buf_puts(&stepbuffer, spec_cur); + retcode = walk_ref_history(out, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer)); + goto cleanup; + } else if (*spec_cur == '^') { + next_state = REVPARSE_STATE_CARET; + } else if (*spec_cur == '~') { + next_state = REVPARSE_STATE_LINEAR; + } else { + git_buf_putc(&specbuffer, *spec_cur); } 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))); + /* Leaving INIT state, find the object specified, in case that state needs it */ + assert(!revparse_lookup_object(&cur_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; + + case REVPARSE_STATE_CARET: + /* Gather characters until NULL, '~', or '^' */ + if (!*spec_cur) { + retcode = handle_caret_syntax(out, + cur_obj, + git_buf_cstr(&stepbuffer)); + goto cleanup; + } else if (*spec_cur == '~') { + retcode = handle_caret_syntax(&cur_obj, + cur_obj, + git_buf_cstr(&stepbuffer)); + if (retcode < 0) goto cleanup; + next_state = REVPARSE_STATE_LINEAR; + } else if (*spec_cur == '^') { + retcode = handle_caret_syntax(&cur_obj, + cur_obj, + git_buf_cstr(&stepbuffer)); + if (retcode < 0) goto cleanup; + next_state = REVPARSE_STATE_CARET; + } else { + git_buf_putc(&stepbuffer, *spec_cur); } spec_cur++; - next_state = REVPARSE_STATE_REF_B; break; - case REVPARSE_STATE_REF_B: - /* Found "@{", gather things until a '}' */ + case REVPARSE_STATE_LINEAR: break; } current_state = next_state; } - - return 0; + +cleanup: + git_buf_free(&specbuffer); + git_buf_free(&stepbuffer); + return retcode; } diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 05c12b074..90aaf5423 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -72,3 +72,19 @@ void test_refs_revparse__describe_output(void) oid_str_cmp(git_object_id(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"); + 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"); +} + +void test_refs_revparse__reflog(void) +{ + // TODO: how to create a fixture for this? git_reflog_write? +}