log -L: :pattern:file syntax to find by funcname
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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
12da1d1f6f
commit
13b8f68c1f
@ -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:
|
||||
|
||||
|
@ -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
|
||||
-----------
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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.
|
||||
+
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
|
||||
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;
|
||||
|
138
line-range.c
138
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,137 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
|
||||
}
|
||||
}
|
||||
|
||||
int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
|
||||
void *cb_data, long lines, long *begin, long *end)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
assert(*arg == ':');
|
||||
term = arg+1;
|
||||
while (*term && *term != ':') {
|
||||
if (*term == '\\' && *(term+1))
|
||||
term++;
|
||||
term++;
|
||||
}
|
||||
if (term == arg+1)
|
||||
return NULL;
|
||||
if (!begin) /* skip_range_arg case */
|
||||
return term;
|
||||
|
||||
pattern = xstrndup(arg+1, term-(arg+1));
|
||||
|
||||
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);
|
||||
free(pattern);
|
||||
|
||||
/* 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,
|
||||
const char *path)
|
||||
{
|
||||
if (*arg == ':') {
|
||||
arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
|
||||
if (!arg || *arg)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
|
||||
|
||||
if (*arg == ',')
|
||||
@ -100,6 +231,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 == ',')
|
||||
|
@ -19,7 +19,8 @@ typedef const char *(*nth_line_fn_t)(void *data, long lno);
|
||||
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
|
||||
|
@ -25,8 +25,10 @@ test_bad_opts () {
|
||||
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
|
||||
|
||||
@ -45,5 +47,7 @@ test_bad_opts "-L 1:simple" "There is no path"
|
||||
test_bad_opts "-L '/foo:b.c'" "argument.*not of the form"
|
||||
test_bad_opts "-L 1000:b.c" "has only.*lines"
|
||||
test_bad_opts "-L 1,1000:b.c" "has only.*lines"
|
||||
test_bad_opts "-L :b.c" "argument.*not of the form"
|
||||
test_bad_opts "-L :foo:b.c" "no match"
|
||||
|
||||
test_done
|
||||
|
100
t/t4211/expect.simple-f-to-main
Normal file
100
t/t4211/expect.simple-f-to-main
Normal file
@ -0,0 +1,100 @@
|
||||
commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:45:41 2013 +0100
|
||||
|
||||
touch comment
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -3,14 +3,14 @@
|
||||
long f(long x)
|
||||
{
|
||||
int s = 0;
|
||||
while (x) {
|
||||
x >>= 1;
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
- * A comment.
|
||||
+ * This is only an example!
|
||||
*/
|
||||
|
||||
|
||||
commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:45:16 2013 +0100
|
||||
|
||||
touch both functions
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -3,14 +3,14 @@
|
||||
-int f(int x)
|
||||
+long f(long x)
|
||||
{
|
||||
int s = 0;
|
||||
while (x) {
|
||||
x >>= 1;
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* A comment.
|
||||
*/
|
||||
|
||||
|
||||
commit f04fb20f2c77850996cba739709acc6faecc58f7
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:44:55 2013 +0100
|
||||
|
||||
change f()
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -3,13 +3,14 @@
|
||||
int f(int x)
|
||||
{
|
||||
int s = 0;
|
||||
while (x) {
|
||||
x >>= 1;
|
||||
s++;
|
||||
}
|
||||
+ return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* A comment.
|
||||
*/
|
||||
|
||||
|
||||
commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:44:48 2013 +0100
|
||||
|
||||
initial
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- /dev/null
|
||||
+++ b/a.c
|
||||
@@ -0,0 +3,13 @@
|
||||
+int f(int x)
|
||||
+{
|
||||
+ int s = 0;
|
||||
+ while (x) {
|
||||
+ x >>= 1;
|
||||
+ s++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * A comment.
|
||||
+ */
|
||||
+
|
70
t/t4211/expect.simple-main-to-end
Normal file
70
t/t4211/expect.simple-main-to-end
Normal file
@ -0,0 +1,70 @@
|
||||
commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:48:43 2013 +0100
|
||||
|
||||
change back to complete line
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -18,5 +18,7 @@
|
||||
int main ()
|
||||
{
|
||||
printf("%ld\n", f(15));
|
||||
return 0;
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
+
|
||||
+/* incomplete lines are bad! */
|
||||
|
||||
commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:48:10 2013 +0100
|
||||
|
||||
change to an incomplete line at end
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -18,5 +18,5 @@
|
||||
int main ()
|
||||
{
|
||||
printf("%ld\n", f(15));
|
||||
return 0;
|
||||
-}
|
||||
+}
|
||||
\ No newline at end of file
|
||||
|
||||
commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:45:16 2013 +0100
|
||||
|
||||
touch both functions
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- a/a.c
|
||||
+++ b/a.c
|
||||
@@ -17,5 +17,5 @@
|
||||
int main ()
|
||||
{
|
||||
- printf("%d\n", f(15));
|
||||
+ printf("%ld\n", f(15));
|
||||
return 0;
|
||||
}
|
||||
|
||||
commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
|
||||
Author: Thomas Rast <trast@student.ethz.ch>
|
||||
Date: Thu Feb 28 10:44:48 2013 +0100
|
||||
|
||||
initial
|
||||
|
||||
diff --git a/a.c b/a.c
|
||||
--- /dev/null
|
||||
+++ b/a.c
|
||||
@@ -0,0 +16,5 @@
|
||||
+int main ()
|
||||
+{
|
||||
+ printf("%d\n", f(15));
|
||||
+ return 0;
|
||||
+}
|
Loading…
Reference in New Issue
Block a user