[PATCH/RFC] Use regex for :/ matching
From: Jeff King <hidden>
Date: 2016-06-15 22:43:55
Subsystem:
documentation, the rest · Maintainers:
Jonathan Corbet, Linus Torvalds
The sha1 syntax :/ used to be a strict prefix match. Instead, let's use a regular expression, which can save on typing. E.g., git show :/"object name: introduce ':/<oneline prefix>'" vs git show :/introduce..:/.oneline Signed-off-by: Jeff King <redacted> --- Obviously this changes the semantics of existing queries. Specifically: - regex metacharacters are now interpreted - the pattern is no longer anchored at the start I find it much more useful than the original, but perhaps it deserves its own syntax instead? I also considered that it might be much slower than the original, but it is not: # original $ time git log :/notfound >/dev/null real 0m1.055s user 0m1.020s sys 0m0.024s # regex $ time git log :/notfound >/dev/null real 0m1.065s user 0m1.028s sys 0m0.036s Curiously, both are much slower than --grep: $ time git log --grep=notfound >/dev/null real 0m0.677s user 0m0.640s sys 0m0.036s And finally, if accepted, a followup patch should change the "prefix" variable to "search" or similar; putting it in here made the diff a bit noisy. Documentation/git-rev-parse.txt | 4 ++-- sha1_name.c | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 329fce0..41dd684 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt@@ -208,8 +208,8 @@ blobs contained in a commit. and dereference the tag recursively until a non-tag object is found. -* A colon, followed by a slash, followed by a text: this names - a commit whose commit message starts with the specified text. +* A colon, followed by a slash, followed by a regular expression: this names + a commit whose commit message starts with a line matching the expression. This name returns the youngest matching commit which is reachable from any ref. If the commit message starts with a '!', you have to repeat that; the special sequence ':/!',
diff --git a/sha1_name.c b/sha1_name.c
index d364244..2ad91a3 100644
--- a/sha1_name.c
+++ b/sha1_name.c@@ -588,8 +588,8 @@ static int handle_one_ref(const char *path, /* * This interprets names like ':/Initial revision of "git"' by searching - * through history and returning the first commit whose message starts - * with the given string. + * through history and returning the first commit whose first line + * matches the given regex. * * For future extension, ':/!' is reserved. If you want to match a message * beginning with a '!', you have to repeat the exclamation mark.
@@ -600,12 +600,22 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) { struct commit_list *list = NULL, *backup = NULL, *l; int retval = -1; + regex_t re; + int r; if (prefix[0] == '!') { if (prefix[1] != '!') die ("Invalid search pattern: %s", prefix); prefix++; } + + r = regcomp(&re, prefix, REG_NOSUB); + if (r != 0) { + char errbuf[256]; + regerror(r, &re, errbuf, sizeof(errbuf)); + return error("unable to compile regex: %s", errbuf); + } + if (!save_commit_buffer) return error("Could not expand oneline-name."); for_each_ref(handle_one_ref, &list);
@@ -613,13 +623,21 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) commit_list_insert(l->item, &backup); while (list) { char *p; + char *end; struct commit *commit; commit = pop_most_recent_commit(&list, ONELINE_SEEN); parse_object(commit->object.sha1); if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n"))) continue; - if (!prefixcmp(p + 2, prefix)) { + p += 2; + end = strchr(p, '\n'); + if (end) + *end = '\0'; + r = regexec(&re, p, 0, NULL, 0); + if (end) + *end = '\n'; + if (r == 0) { hashcpy(sha1, commit->object.sha1); retval = 0; break;
@@ -628,6 +646,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) free_commit_list(list); for (l = backup; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); + regfree(&re); return retval; }
--
1.5.3.7.2068.g5949-dirty