Code

Merge branch 'rs/diff-whole-function'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Oct 2011 17:49:13 +0000 (10:49 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Oct 2011 17:49:13 +0000 (10:49 -0700)
* rs/diff-whole-function:
  diff: add option to show whole functions as context
  xdiff: factor out get_func_line()

Documentation/diff-options.txt
diff.c
diff.h
t/t4051-diff-function-context.sh [new file with mode: 0644]
xdiff/xdiff.h
xdiff/xemit.c

index 5c53bdba948ea817e01e169a9fc183d4fc2d1313..08b581f040859d39aeaad8294bc7a3e7760ab0b9 100644 (file)
@@ -408,6 +408,10 @@ endif::git-format-patch[]
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
 
+-W::
+--function-context::
+       Show whole surrounding functions of changes.
+
 ifndef::git-format-patch[]
 --exit-code::
        Make the program exit with codes similar to diff(1).
diff --git a/diff.c b/diff.c
index d922b77aef2da84824a8e14fc21961e36e6d2e36..374ecf3b486e218c52f94ba14481a715c19b80db 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2169,6 +2169,8 @@ static void builtin_diff(const char *name_a,
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
+               if (DIFF_OPT_TST(o, FUNCCONTEXT))
+                       xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
@@ -3536,6 +3538,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (opt_arg(arg, '\0', "inter-hunk-context",
                         &options->interhunkcontext))
                ;
+       else if (!strcmp(arg, "-W"))
+               DIFF_OPT_SET(options, FUNCCONTEXT);
+       else if (!strcmp(arg, "--function-context"))
+               DIFF_OPT_SET(options, FUNCCONTEXT);
+       else if (!strcmp(arg, "--no-function-context"))
+               DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                options->file = fopen(optarg, "w");
                if (!options->file)
diff --git a/diff.h b/diff.h
index 8c66b59517305546d3e2f66fca652284468e1da1..0c51724493f76461b67d6ef8f5556852819ad809 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -79,6 +79,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
 #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
 #define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
+#define DIFF_OPT_FUNCCONTEXT         (1 << 29)
 
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
new file mode 100644 (file)
index 0000000..001d678
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff function context'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+
+cat <<\EOF >hello.c
+#include <stdio.h>
+
+static int a(void)
+{
+       /*
+        * Dummy.
+        */
+}
+
+static int hello_world(void)
+{
+       /* Classic. */
+       printf("Hello world.\n");
+
+       /* Success! */
+       return 0;
+}
+static int b(void)
+{
+       /*
+        * Dummy, too.
+        */
+}
+
+int main(int argc, char **argv)
+{
+       a();
+       b();
+       return hello_world();
+}
+EOF
+
+test_expect_success 'setup' '
+       git add hello.c &&
+       test_tick &&
+       git commit -m initial &&
+
+       grep -v Classic <hello.c >hello.c.new &&
+       mv hello.c.new hello.c
+'
+
+cat <<\EOF >expected
+diff --git a/hello.c b/hello.c
+--- a/hello.c
++++ b/hello.c
+@@ -10,8 +10,7 @@ static int a(void)
+ static int hello_world(void)
+ {
+-      /* Classic. */
+       printf("Hello world.\n");
+       /* Success! */
+       return 0;
+ }
+EOF
+
+test_expect_success 'diff -U0 -W' '
+       git diff -U0 -W >actual &&
+       compare_diff_patch actual expected
+'
+
+cat <<\EOF >expected
+diff --git a/hello.c b/hello.c
+--- a/hello.c
++++ b/hello.c
+@@ -9,9 +9,8 @@ static int a(void)
+ static int hello_world(void)
+ {
+-      /* Classic. */
+       printf("Hello world.\n");
+       /* Success! */
+       return 0;
+ }
+EOF
+
+test_expect_success 'diff -W' '
+       git diff -W >actual &&
+       compare_diff_patch actual expected
+'
+
+test_done
index 4beb10c678702a34b14914f8b292dd65b5c273cd..00d36c3ac7a7642831d2ffc49647caf77a4d066c 100644 (file)
@@ -43,6 +43,7 @@ extern "C" {
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
 #define XDL_MMB_READONLY (1 << 0)
 
index 277e2eec5b4cced5882f37ac42af7f22efe92902..2e669c3e2570332ee50a72e06a77ad4a356ecfd6 100644 (file)
@@ -100,14 +100,40 @@ static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        return 0;
 }
 
+struct func_line {
+       long len;
+       char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+                         struct func_line *func_line, long start, long limit)
+{
+       find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
+       long l, size, step = (start > limit) ? -1 : 1;
+       char *buf, dummy[1];
+
+       buf = func_line ? func_line->buf : dummy;
+       size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+       for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+               const char *rec;
+               long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+               long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+               if (len >= 0) {
+                       if (func_line)
+                               func_line->len = len;
+                       return l;
+               }
+       }
+       return -1;
+}
+
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg) {
        long s1, s2, e1, e2, lctx;
        xdchange_t *xch, *xche;
-       char funcbuf[80];
-       long funclen = 0;
        long funclineprev = -1;
-       find_func_t ff = xecfg->find_func ?  xecfg->find_func : def_ff;
+       struct func_line func_line = { 0 };
 
        if (xecfg->flags & XDL_EMIT_COMMON)
                return xdl_emit_common(xe, xscr, ecb, xecfg);
@@ -118,6 +144,17 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
                s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
 
+               if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+                       long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+                       if (fs1 < 0)
+                               fs1 = 0;
+                       if (fs1 < s1) {
+                               s2 -= s1 - fs1;
+                               s1 = fs1;
+                       }
+               }
+
+ again:
                lctx = xecfg->ctxlen;
                lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
                lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
@@ -125,27 +162,43 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                e1 = xche->i1 + xche->chg1 + lctx;
                e2 = xche->i2 + xche->chg2 + lctx;
 
+               if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+                       long fe1 = get_func_line(xe, xecfg, NULL,
+                                                xche->i1 + xche->chg1,
+                                                xe->xdf1.nrec);
+                       if (fe1 < 0)
+                               fe1 = xe->xdf1.nrec;
+                       if (fe1 > e1) {
+                               e2 += fe1 - e1;
+                               e1 = fe1;
+                       }
+
+                       /*
+                        * Overlap with next change?  Then include it
+                        * in the current hunk and start over to find
+                        * its new end.
+                        */
+                       if (xche->next) {
+                               long l = xche->next->i1;
+                               if (l <= e1 ||
+                                   get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+                                       xche = xche->next;
+                                       goto again;
+                               }
+                       }
+               }
+
                /*
                 * Emit current hunk header.
                 */
 
                if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
-                       long l;
-                       for (l = s1 - 1; l >= 0 && l > funclineprev; l--) {
-                               const char *rec;
-                               long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
-                               long newfunclen = ff(rec, reclen, funcbuf,
-                                                    sizeof(funcbuf),
-                                                    xecfg->find_func_priv);
-                               if (newfunclen >= 0) {
-                                       funclen = newfunclen;
-                                       break;
-                               }
-                       }
+                       get_func_line(xe, xecfg, &func_line,
+                                     s1 - 1, funclineprev);
                        funclineprev = s1 - 1;
                }
                if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
-                                     funcbuf, funclen, ecb) < 0)
+                                     func_line.buf, func_line.len, ecb) < 0)
                        return -1;
 
                /*