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 <tr...@student.ethz.ch>
---
 Documentation/line-range-format.txt |   7 ++
 builtin/blame.c                     |   2 +-
 line-log.c                          | 130 +++++++++++++++++++++++++++++++++++-
 line-log.h                          |   3 +-
 t/t4211-line-log.sh                 |   3 +-
 t/t4211/expect.simple-f-to-main     | 100 +++++++++++++++++++++++++++
 t/t4211/expect.simple-main-to-end   |  70 +++++++++++++++++++
 7 files changed, 310 insertions(+), 5 deletions(-)
 create mode 100644 t/t4211/expect.simple-f-to-main
 create mode 100644 t/t4211/expect.simple-main-to-end

diff --git a/Documentation/line-range-format.txt 
b/Documentation/line-range-format.txt
index 9ce0688..5279a76 100644
--- a/Documentation/line-range-format.txt
+++ b/Documentation/line-range-format.txt
@@ -22,3 +22,10 @@ of lines before or after the line given by <start>.
 A literal dollar sign can be used as a shorthand for the last line in
 the file.
 +
+
+- :regex
++
+If the entire range 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 c933b15..b7d210f 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 789b7c4..a74bbaf 100644
--- a/line-log.c
+++ b/line-log.c
@@ -11,6 +11,7 @@
 #include "strbuf.h"
 #include "log-tree.h"
 #include "graph.h"
+#include "userdiff.h"
 #include "line-log.h"
 
 void range_set_grow (struct range_set *rs, size_t extra)
@@ -531,9 +532,130 @@ 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(&regexp, pattern, REG_NEWLINE);
+       if (reg_error) {
+               char errbuf[1024];
+               regerror(reg_error, &regexp, errbuf, 1024);
+               die("-L parameter '%s': %s", pattern, errbuf);
+       }
+
+       p = find_funcname_matching_regexp(xecfg, (char*) start, &regexp);
+       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(&regexp);
+       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;
+       }
+
        arg = parse_loc(arg, nth_line_cb, cb_data, lines, -1, begin);
 
        if (*arg == ',') {
@@ -553,6 +675,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, 0);
 
        if (*arg == ',')
@@ -681,7 +806,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-log.h b/line-log.h
index e3ec094..071e739 100644
--- a/line-log.h
+++ b/line-log.h
@@ -20,7 +20,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 639a16f..d488690 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -13,14 +13,15 @@ canned_test () {
                git log $1 >actual &&
                test_cmp \"\$TEST_DIRECTORY\"/t4211/expect.$2 actual
        "
-       cp actual "$TEST_DIRECTORY"/t4211/expect.$2
 }
 
 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
 canned_test "-L 0,+5:a.c simple" beginning-of-file
diff --git a/t/t4211/expect.simple-f-to-main b/t/t4211/expect.simple-f-to-main
new file mode 100644
index 0000000..a475768
--- /dev/null
+++ b/t/t4211/expect.simple-f-to-main
@@ -0,0 +1,100 @@
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <tr...@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 <tr...@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 <tr...@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 <tr...@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.
++ */
++
diff --git a/t/t4211/expect.simple-main-to-end 
b/t/t4211/expect.simple-main-to-end
new file mode 100644
index 0000000..8480bd9
--- /dev/null
+++ b/t/t4211/expect.simple-main-to-end
@@ -0,0 +1,70 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <tr...@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 <tr...@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 <tr...@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 <tr...@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;
++}
-- 
1.8.2.rc1.388.g1bd82c8

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to