From: Thomas Rast <trast@student.ethz.ch>
To: <git@vger.kernel.org>
Cc: "Junio C Hamano" <gitster@pobox.com>,
"Bo Yang" <struggleyb.nku@gmail.com>,
"Zbigniew Jędrzejewski-Szmek" <zbyszek@in.waw.pl>,
"Will Palmer" <wmpalmer@gmail.com>
Subject: [PATCH v9 4/5] log -L: :pattern:file syntax to find by funcname
Date: Thu, 21 Mar 2013 13:52:39 +0100 [thread overview]
Message-ID: <153eaec7aff9618de3c30aa0b21556e1ed512e12.1363865444.git.trast@student.ethz.ch> (raw)
In-Reply-To: <cover.1363865444.git.trast@student.ethz.ch>
This new syntax finds a funcname matching /pattern/, and then takes from there
up to (but not including) the next funcname. So you can say
git log -L:main:main.c
and it will dig up the main() function and show its line-log, provided
there are no other funcnames matching 'main'.
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
Documentation/blame-options.txt | 2 +-
Documentation/git-blame.txt | 6 +-
Documentation/git-log.txt | 11 +--
Documentation/line-range-format.txt | 7 ++
builtin/blame.c | 2 +-
line-log.c | 5 +-
line-range.c | 130 +++++++++++++++++++++++++++++++++++-
line-range.h | 3 +-
t/t4211-line-log.sh | 2 +
9 files changed, 154 insertions(+), 14 deletions(-)
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 6998d9f..e9f984b 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -9,7 +9,7 @@
--show-stats::
Include additional statistics at the end of blame output.
--L <start>,<end>::
+-L <start>,<end>, -L :<regex>::
Annotate only the given line range. <start> and <end> can take
one of these forms:
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 9a05c2b..6cea7f1 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -8,9 +8,9 @@ git-blame - Show what revision and author last modified each line of a file
SYNOPSIS
--------
[verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
- [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
- [<rev> | --contents <file> | --reverse <rev>] [--] <file>
+'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
+ [-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
+ [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file>
DESCRIPTION
-----------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 8727c60..4850226 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -69,12 +69,13 @@ produced by --stat etc.
Note that only message is considered, if also a diff is shown
its size is not included.
--L <start>,<end>:<file>::
+-L <start>,<end>:<file>, -L :<regex>:<file>::
+
Trace the evolution of the line range given by "<start>,<end>"
- within the <file>. You may not give any pathspec limiters.
- This is currently limited to a walk starting from a single
- revision, i.e., you may only give zero or one positive
- revision arguments.
+ (or the funcname regex <regex>) within the <file>. You may
+ not give any pathspec limiters. This is currently limited to
+ a walk starting from a single revision, i.e., you may only
+ give zero or one positive revision arguments.
<start> and <end> can take one of these forms:
diff --git a/Documentation/line-range-format.txt b/Documentation/line-range-format.txt
index 265bc23..3e7ce72 100644
--- a/Documentation/line-range-format.txt
+++ b/Documentation/line-range-format.txt
@@ -16,3 +16,10 @@ starting at the line given by <start>.
This is only valid for <end> and will specify a number
of lines before or after the line given by <start>.
+
+
+- :regex
++
+If the option's argument is of the form :regex, it denotes the range
+from the first funcname line that matches <regex>, up to the next
+funcname line.
++
diff --git a/builtin/blame.c b/builtin/blame.c
index 20eb439..1c09d55 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1940,7 +1940,7 @@ static void prepare_blame_range(struct scoreboard *sb,
long lno,
long *bottom, long *top)
{
- if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top))
+ if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path))
usage(blame_usage);
}
diff --git a/line-log.c b/line-log.c
index 29162fc..81c8d74 100644
--- a/line-log.c
+++ b/line-log.c
@@ -12,6 +12,7 @@
#include "strbuf.h"
#include "log-tree.h"
#include "graph.h"
+#include "userdiff.h"
#include "line-log.h"
static void range_set_grow(struct range_set *rs, size_t extra)
@@ -438,7 +439,6 @@ static void range_set_map_across_diff(struct range_set *out,
*touched_out = touched;
}
-
static struct commit *check_single_commit(struct rev_info *revs)
{
struct object *commit = NULL;
@@ -559,7 +559,8 @@ static const char *nth_line(void *data, long line)
cb_data.line_ends = ends;
if (parse_range_arg(range_part, nth_line, &cb_data,
- lines, &begin, &end))
+ lines, &begin, &end,
+ spec->path))
die("malformed -L argument '%s'", range_part);
if (begin < 1)
begin = 1;
diff --git a/line-range.c b/line-range.c
index d0c7dac..ea141ab 100644
--- a/line-range.c
+++ b/line-range.c
@@ -1,5 +1,8 @@
#include "git-compat-util.h"
#include "line-range.h"
+#include "xdiff-interface.h"
+#include "strbuf.h"
+#include "userdiff.h"
/*
* Parse one item in the -L option
@@ -84,9 +87,131 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
}
}
+static int match_funcname(xdemitconf_t *xecfg, const char *bol, const char *eol)
+{
+ if (xecfg) {
+ char buf[1];
+ return xecfg->find_func(bol, eol - bol, buf, 1,
+ xecfg->find_func_priv) >= 0;
+ }
+
+ if (bol == eol)
+ return 0;
+ if (isalpha(*bol) || *bol == '_' || *bol == '$')
+ return 1;
+ return 0;
+}
+
+static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start,
+ regex_t *regexp)
+{
+ int reg_error;
+ regmatch_t match[1];
+ while (1) {
+ const char *bol, *eol;
+ reg_error = regexec(regexp, start, 1, match, 0);
+ if (reg_error == REG_NOMATCH)
+ return NULL;
+ else if (reg_error) {
+ char errbuf[1024];
+ regerror(reg_error, regexp, errbuf, 1024);
+ die("-L parameter: regexec() failed: %s", errbuf);
+ }
+ /* determine extent of line matched */
+ bol = start+match[0].rm_so;
+ eol = start+match[0].rm_eo;
+ while (bol > start && *bol != '\n')
+ bol--;
+ if (*bol == '\n')
+ bol++;
+ while (*eol && *eol != '\n')
+ eol++;
+ if (*eol == '\n')
+ eol++;
+ /* is it a funcname line? */
+ if (match_funcname(xecfg, (char*) bol, (char*) eol))
+ return bol;
+ start = eol;
+ }
+}
+
+static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
+ void *cb_data, long lines, long *begin, long *end,
+ const char *path)
+{
+ const char *pattern;
+ const char *term;
+ struct userdiff_driver *drv;
+ xdemitconf_t *xecfg = NULL;
+ const char *start;
+ const char *p;
+ int reg_error;
+ regex_t regexp;
+
+ pattern = arg+1;
+ term = (char*) strchr(pattern, ':');
+ if (term) {
+ assert(!begin);
+ return term;
+ }
+ /* all of the rest is the regex */
+ term = pattern + strlen(pattern);
+
+ start = nth_line_cb(cb_data, 0);
+
+ drv = userdiff_find_by_path(path);
+ if (drv && drv->funcname.pattern) {
+ const struct userdiff_funcname *pe = &drv->funcname;
+ xecfg = xcalloc(1, sizeof(*xecfg));
+ xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
+ }
+
+ reg_error = regcomp(®exp, pattern, REG_NEWLINE);
+ if (reg_error) {
+ char errbuf[1024];
+ regerror(reg_error, ®exp, errbuf, 1024);
+ die("-L parameter '%s': %s", pattern, errbuf);
+ }
+
+ p = find_funcname_matching_regexp(xecfg, (char*) start, ®exp);
+ if (!p)
+ die("-L parameter '%s': no match", pattern);
+ *begin = 0;
+ while (p > nth_line_cb(cb_data, *begin))
+ (*begin)++;
+
+ if (*begin >= lines)
+ die("-L parameter '%s' matches at EOF", pattern);
+
+ *end = *begin+1;
+ while (*end < lines) {
+ const char *bol = nth_line_cb(cb_data, *end);
+ const char *eol = nth_line_cb(cb_data, *end+1);
+ if (match_funcname(xecfg, bol, eol))
+ break;
+ (*end)++;
+ }
+
+ regfree(®exp);
+ free(xecfg);
+
+ /* compensate for 1-based numbering */
+ (*begin)++;
+
+ return term;
+}
+
int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
- void *cb_data, long lines, long *begin, long *end)
+ void *cb_data, long lines, long *begin, long *end,
+ const char *path)
{
+ if (*arg == ':') {
+ arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
+ if (*arg)
+ return -1;
+ return 0;
+ }
+
arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
if (*arg == ',')
@@ -100,6 +225,9 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
const char *skip_range_arg(const char *arg)
{
+ if (*arg == ':')
+ return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL);
+
arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
if (*arg == ',')
diff --git a/line-range.h b/line-range.h
index 88aaf08..ae3d012 100644
--- a/line-range.h
+++ b/line-range.h
@@ -19,7 +19,8 @@
extern int parse_range_arg(const char *arg,
nth_line_fn_t nth_line_cb,
void *cb_data, long lines,
- long *begin, long *end);
+ long *begin, long *end,
+ const char *path);
/*
* Scan past a range argument that could be parsed by
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 286390d..096679d 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -18,8 +18,10 @@ canned_test () {
canned_test "-L 4,12:a.c simple" simple-f
canned_test "-L 4,+9:a.c simple" simple-f
canned_test "-L '/long f/,/^}/:a.c' simple" simple-f
+canned_test "-L :f:a.c simple" simple-f-to-main
canned_test "-L '/main/,/^}/:a.c' simple" simple-main
+canned_test "-L :main:a.c simple" simple-main-to-end
canned_test "-L 1,+4:a.c simple" beginning-of-file
--
1.8.2.241.gee8bb87
next prev parent reply other threads:[~2013-03-21 12:53 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-21 12:52 [PATCH v9 0/5] git log -L Thomas Rast
2013-03-21 12:52 ` [PATCH v9 1/5] Refactor parse_loc Thomas Rast
2013-03-21 12:52 ` [PATCH v9 2/5] Export rewrite_parents() for 'log -L' Thomas Rast
2013-03-21 12:52 ` [PATCH v9 3/5] Implement line-history search (git log -L) Thomas Rast
2013-03-21 19:05 ` Junio C Hamano
2013-03-23 6:00 ` Thomas Rast
2013-03-21 12:52 ` Thomas Rast [this message]
2013-03-21 12:52 ` [PATCH v9 5/5] Speed up log -L... -M Thomas Rast
2013-03-21 21:11 ` Eric Sunshine
2013-03-23 5:58 ` Thomas Rast
2013-03-23 9:04 ` Jeff King
2013-03-24 7:38 ` Eric Sunshine
2013-03-23 6:44 ` [PATCH v9a 0/5] git log -L Thomas Rast
2013-03-23 6:44 ` [PATCH v9a 1/5] Refactor parse_loc Thomas Rast
2013-03-23 6:44 ` [PATCH v9a 2/5] Export rewrite_parents() for 'log -L' Thomas Rast
2013-03-23 6:44 ` [PATCH v9a 3/5] Implement line-history search (git log -L) Thomas Rast
2013-03-23 10:31 ` Antoine Pelisse
2013-03-23 10:32 ` Antoine Pelisse
2013-03-28 16:47 ` [PATCH v10 0/5] git log -L Thomas Rast
2013-03-28 16:47 ` [PATCH v10 1/5] Refactor parse_loc Thomas Rast
2013-03-28 16:47 ` [PATCH v10 2/5] Export rewrite_parents() for 'log -L' Thomas Rast
2013-03-28 16:47 ` [PATCH v10 3/5] Implement line-history search (git log -L) Thomas Rast
2013-03-28 16:47 ` [PATCH v10 4/5] log -L: :pattern:file syntax to find by funcname Thomas Rast
2013-03-28 16:47 ` [PATCH v10 5/5] Speed up log -L... -M Thomas Rast
2013-03-23 6:44 ` [PATCH v9a 4/5] log -L: :pattern:file syntax to find by funcname Thomas Rast
2013-03-23 6:44 ` [PATCH v9a 5/5] Speed up log -L... -M Thomas Rast
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=153eaec7aff9618de3c30aa0b21556e1ed512e12.1363865444.git.trast@student.ethz.ch \
--to=trast@student.ethz.ch \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=struggleyb.nku@gmail.com \
--cc=wmpalmer@gmail.com \
--cc=zbyszek@in.waw.pl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).