Rev-parse: implement ":/foo" syntax.
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
diff --git a/src/revparse.c b/src/revparse.c
index 60e819c..6d88a1d 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 9ecdb82..0bd6ad7 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");
}