diff --git a/src/revparse.c b/src/revparse.c index e03e3338c..547426449 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -20,6 +20,7 @@ #include "git2/refs.h" #include "git2/repository.h" #include "git2/config.h" +#include "git2/revwalk.h" GIT_BEGIN_DECL @@ -294,7 +295,7 @@ static git_otype parse_obj_type(const char *str) return GIT_OBJ_BAD; } -static int handle_caret_syntax(git_object **out, git_object *obj, const char *movement) +static int handle_caret_syntax(git_object **out, git_repository *repo, git_object *obj, const char *movement) { git_commit *commit; size_t movementlen = strlen(movement); @@ -325,8 +326,48 @@ static int handle_caret_syntax(git_object **out, git_object *obj, const char *mo /* {/...} -> Walk all commits until we see a commit msg that matches the phrase. */ if (movement[1] == '/') { - /* TODO */ - return GIT_ERROR; + int retcode = GIT_ERROR; + git_revwalk *walk; + if (!git_revwalk_new(&walk, repo)) { + git_oid oid; + regex_t preg; + git_buf buf = GIT_BUF_INIT; + + git_revwalk_sorting(walk, GIT_SORT_TIME); + git_revwalk_push(walk, git_object_id(obj)); + + /* Extract the regex from the movement string */ + git_buf_put(&buf, movement+2, strlen(movement)-3); + + if (!regcomp(&preg, git_buf_cstr(&buf), REG_EXTENDED)) { + while(!git_revwalk_next(&oid, walk)) { + git_object *walkobj; + char str[41]; + git_oid_fmt(str, &oid); + str[40] = 0; + + /* Fetch the commit object, and check for matches in the message */ + if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { + if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { + /* Found it! */ + retcode = 0; + *out = walkobj; + if (obj == walkobj) { + /* Avoid leaking an object */ + git_object_free(walkobj); + } + break; + } + git_object_free(walkobj); + } + } + regfree(&preg); + } + + git_buf_free(&buf); + git_revwalk_free(walk); + } + return retcode; } /* {...} -> Dereference until we reach an object of a certain type. */ @@ -444,14 +485,14 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec case REVPARSE_STATE_CARET: /* Gather characters until NULL, '~', or '^' */ if (!*spec_cur) { - retcode = handle_caret_syntax(out, cur_obj, git_buf_cstr(&stepbuffer)); + retcode = handle_caret_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer)); next_state = REVPARSE_STATE_DONE; } else if (*spec_cur == '~') { - retcode = handle_caret_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); git_buf_clear(&stepbuffer); next_state = !retcode ? REVPARSE_STATE_LINEAR : REVPARSE_STATE_DONE; } else if (*spec_cur == '^') { - retcode = handle_caret_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer)); + retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer)); git_buf_clear(&stepbuffer); if (retcode < 0) { next_state = REVPARSE_STATE_DONE; diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 944a18b9c..e967f7a48 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -122,6 +122,21 @@ void test_refs_revparse__reflog(void) test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); +} + +void test_refs_revparse__revwalk(void) +{ + cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/not found in any commit}")); + cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/merge}")); + + test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f"); + test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a"); +} + +void test_refs_revparse__date(void) +{ /* Not ready yet test_object("HEAD@{100 years ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("master@{2012-4-30 10:23:20}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");