From 734efe4b8e8f6cfc820c7f88b3fb4e4221cec0ab Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Fri, 1 Jun 2012 14:18:52 -0700 Subject: [PATCH] Rev-parse: implement ":/foo" syntax. --- src/revparse.c | 52 ++++++++++++++++++++++++++++++++++---- tests-clar/refs/revparse.c | 6 ++++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/revparse.c b/src/revparse.c index 60e819c00..6d88a1d38 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -432,9 +432,6 @@ static int handle_caret_syntax(git_object **out, git_repository *repo, git_objec } else { 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)) { @@ -580,6 +577,49 @@ static int handle_colon_syntax(git_object **out, return git_tree_entry_2object(out, repo, entry); } +static int git__revparse_global_grep(git_object **out, git_repository *repo, const char *pattern) +{ + git_revwalk *walk; + int retcode = GIT_ERROR; + + if (!git_revwalk_new(&walk, repo)) { + regex_t preg; + int reg_error; + git_oid oid; + + git_revwalk_sorting(walk, GIT_SORT_TIME); + git_revwalk_push_glob(walk, "refs/heads/*"); + + reg_error = regcomp(&preg, pattern, REG_EXTENDED); + if (reg_error != 0) { + giterr_set_regex(&preg, reg_error); + } else { + git_object *walkobj = NULL, *resultobj = NULL; + while(!git_revwalk_next(&oid, walk)) { + /* Fetch the commit object, and check for matches in the message */ + if (walkobj != resultobj) git_object_free(walkobj); + if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) { + if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) { + /* Match! */ + resultobj = walkobj; + retcode = 0; + break; + } + } + } + if (!resultobj) { + giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", pattern); + } else { + *out = resultobj; + } + regfree(&preg); + git_revwalk_free(walk); + } + } + + return retcode; +} + 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; @@ -591,8 +631,10 @@ 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. */ + if (spec[1] == '/') { + return git__revparse_global_grep(out, repo, spec+2); + } + /* TODO: support merge-stage path lookup (":2:Makefile"). */ giterr_set(GITERR_INVALID, "Unimplemented"); return GIT_ERROR; } diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 9ecdb8265..0bd6ad704 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -156,11 +156,15 @@ void test_refs_revparse__date(void) 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, ":/")); + cl_git_fail(git_revparse_single(&g_obj, g_repo, ":/not found in any commit")); cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f"); test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b"); test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6"); test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"); + test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f"); + test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd"); + test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9"); }