diff options
-rw-r--r-- | src/revparse.c | 49 | ||||
-rw-r--r-- | tests-clar/refs/revparse.c | 22 |
2 files changed, 71 insertions, 0 deletions
diff --git a/src/revparse.c b/src/revparse.c index ffa3e6c0d..7a07e0c38 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -260,6 +260,45 @@ static int handle_caret_syntax(git_object **out, git_object *obj, const char *mo return 0; } +static int handle_linear_syntax(git_object **out, git_object *obj, const char *movement) +{ + git_commit *commit1, *commit2; + int i, n; + + /* Dereference until we reach a commit. */ + if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) { + /* Can't dereference to a commit; fail */ + return GIT_ERROR; + } + + /* "~" is the same as "~1" */ + if (strlen(movement) == 0) { + n = 1; + } else { + git__strtol32(&n, movement, NULL, 0); + } + commit1 = (git_commit*)obj; + + /* "~0" just returns the input */ + if (n == 0) { + *out = obj; + return 0; + } + + for (i=0; i<n; i++) { + if (git_commit_parent(&commit2, commit1, 0) < 0) { + return GIT_ERROR; + } + if (commit1 != (git_commit*)obj) { + git_commit_free(commit1); + } + commit1 = commit2; + } + + *out = (git_object*)commit1; + return 0; +} + int git_revparse_single(git_object **out, git_repository *repo, const char *spec) { revparse_state current_state = REVPARSE_STATE_INIT; @@ -327,8 +366,18 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec case REVPARSE_STATE_LINEAR: if (!*spec_cur) { + retcode = handle_linear_syntax(out, cur_obj, git_buf_cstr(&stepbuffer)); + next_state = REVPARSE_STATE_DONE; } else if (*spec_cur == '~') { + retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + if (retcode < 0) { + next_state = REVPARSE_STATE_DONE; + } } else if (*spec_cur == '^') { + retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + git_buf_clear(&stepbuffer); + next_state = !retcode ? REVPARSE_STATE_CARET : REVPARSE_STATE_DONE; } else { git_buf_putc(&stepbuffer, *spec_cur); } diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index e34646b11..08cf406f6 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -109,6 +109,28 @@ void test_refs_revparse__to_type(void) cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); } +void test_refs_revparse__linear_history(void) +{ + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~0")); + oid_str_cmp(g_obj, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~1")); + oid_str_cmp(g_obj, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~2")); + oid_str_cmp(g_obj, "9fd738e8f7967c078dceed8190330fc8648ee56a"); + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~1~1")); + oid_str_cmp(g_obj, "9fd738e8f7967c078dceed8190330fc8648ee56a"); +} + +void test_refs_revparse__chaining(void) +{ + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~1^1")); + oid_str_cmp(g_obj, "9fd738e8f7967c078dceed8190330fc8648ee56a"); + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master~1^2")); + oid_str_cmp(g_obj, "c47800c7266a2be04c571c04d5a6614691ea99bd"); + cl_git_pass(git_revparse_single(&g_obj, g_repo, "master^1^2~1")); + oid_str_cmp(g_obj, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); +} + void test_refs_revparse__reflog(void) { /* TODO: how to create a fixture for this? git_reflog_write? */ |