Commit 6841bf1343d4d3bbafbc1db614429635309c20d4

Klemens Nanni 2019-11-29T21:54:03

log: Implement -s search-pattern match_logmsg() is copied from tog's match_commit().

diff --git a/got/got.1 b/got/got.1
index 9c0c377..59c4169 100644
--- a/got/got.1
+++ b/got/got.1
@@ -314,7 +314,7 @@ in a pattern.
 .It Cm st
 Short alias for
 .Cm status .
-.It Cm log Oo Fl c Ar commit Oc Oo Fl C Ar number Oc Oo Fl f Oc Oo Fl l Ar N Oc Oo Fl p Oc Oo Fl r Ar repository-path Oc Op Ar path
+.It Cm log Oo Fl c Ar commit Oc Oo Fl C Ar number Oc Oo Fl f Oc Oo Fl l Ar N Oc Oo Fl p Oc Oo Fl g Ar search-pattern Oc Oo Fl r Ar repository-path Oc Op Ar path
 Display history of a repository.
 If a
 .Ar path
@@ -354,6 +354,12 @@ Display the patch of modifications made in each commit.
 If a
 .Ar path
 is specified, only show the patch of modifications at or within this path.
+.It Fl g Ar search-pattern
+If specified, show only commits which log message matches the extended
+regular expression
+.Ar search-pattern .
+Regular expression syntax is documented in
+.Xr re_format 7 .
 .It Fl r Ar repository-path
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
diff --git a/got/got.c b/got/got.c
index 9f40340..a563bd3 100644
--- a/got/got.c
+++ b/got/got.c
@@ -34,6 +34,7 @@
 #include <libgen.h>
 #include <time.h>
 #include <paths.h>
+#include <regex.h>
 
 #include "got_version.h"
 #include "got_error.h"
@@ -1589,6 +1590,32 @@ get_datestr(time_t *time, char *datebuf)
 	return s;
 }
 
+static const struct got_error *
+match_logmsg(int *have_match, struct got_object_id *id,
+    struct got_commit_object *commit, regex_t *regex)
+{
+	const struct got_error *err = NULL;
+	regmatch_t regmatch;
+	char *id_str = NULL, *logmsg = NULL;
+
+	*have_match = 0;
+
+	err = got_object_id_str(&id_str, id);
+	if (err)
+		return err;
+
+	err = got_object_commit_get_logmsg(&logmsg, commit);
+	if (err)
+		goto done;
+
+	if (regexec(regex, logmsg, 1, &regmatch, 0) == 0)
+		*have_match = 1;
+done:
+	free(id_str);
+	free(logmsg);
+	return err;
+}
+
 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
 
 static const struct got_error *
@@ -1705,11 +1732,17 @@ print_commit(struct got_commit_object *commit, struct got_object_id *id,
 
 static const struct got_error *
 print_commits(struct got_object_id *root_id, struct got_repository *repo,
-    char *path, int show_patch, int diff_context, int limit,
-    int first_parent_traversal, struct got_reflist_head *refs)
+    char *path, int show_patch, char *search_pattern, int diff_context,
+    int limit, int first_parent_traversal, struct got_reflist_head *refs)
 {
 	const struct got_error *err;
 	struct got_commit_graph *graph;
+	regex_t regex;
+	int have_match;
+
+	if (search_pattern &&
+	    regcomp(&regex, search_pattern, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
+		return got_error_msg(GOT_ERR_REGEX, search_pattern);
 
 	err = got_commit_graph_open(&graph, root_id, path,
 	    first_parent_traversal, repo);
@@ -1747,6 +1780,19 @@ print_commits(struct got_object_id *root_id, struct got_repository *repo,
 		err = got_object_open_as_commit(&commit, repo, id);
 		if (err)
 			break;
+
+		if (search_pattern) {
+			err = match_logmsg(&have_match, id, commit, &regex);
+			if (err) {
+				got_object_commit_close(commit);
+				break;
+			}
+			if (have_match == 0) {
+				got_object_commit_close(commit);
+				continue;
+			}
+		}
+
 		err = print_commit(commit, id, repo, path, show_patch,
 		    diff_context, refs);
 		got_object_commit_close(commit);
@@ -1754,6 +1800,8 @@ print_commits(struct got_object_id *root_id, struct got_repository *repo,
 			break;
 	}
 done:
+	if (search_pattern)
+		regfree(&regex);
 	got_commit_graph_close(graph);
 	return err;
 }
@@ -1762,7 +1810,7 @@ __dead static void
 usage_log(void)
 {
 	fprintf(stderr, "usage: %s log [-c commit] [-C number] [-f] [ -l N ] [-p] "
-	    "[-r repository-path] [path]\n", getprogname());
+	    "[-s search-pattern] [-r repository-path] [path]\n", getprogname());
 	exit(1);
 }
 
@@ -1791,7 +1839,7 @@ cmd_log(int argc, char *argv[])
 	struct got_commit_object *commit = NULL;
 	struct got_object_id *id = NULL;
 	char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
-	char *start_commit = NULL;
+	char *start_commit = NULL, *search_pattern = NULL;
 	int diff_context = 3, ch;
 	int show_patch = 0, limit = 0, first_parent_traversal = 0;
 	const char *errstr;
@@ -1808,7 +1856,7 @@ cmd_log(int argc, char *argv[])
 
 	limit = get_default_log_limit();
 
-	while ((ch = getopt(argc, argv, "b:pc:C:l:fr:")) != -1) {
+	while ((ch = getopt(argc, argv, "b:pc:C:l:fr:s:")) != -1) {
 		switch (ch) {
 		case 'p':
 			show_patch = 1;
@@ -1837,6 +1885,9 @@ cmd_log(int argc, char *argv[])
 				    optarg);
 			got_path_strip_trailing_slashes(repo_path);
 			break;
+		case 's':
+			search_pattern = optarg;
+			break;
 		default:
 			usage_log();
 			/* NOTREACHED */
@@ -1982,7 +2033,7 @@ cmd_log(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = print_commits(id, repo, path, show_patch,
+	error = print_commits(id, repo, path, show_patch, search_pattern,
 	    diff_context, limit, first_parent_traversal, &refs);
 done:
 	free(path);