Skip to content

Commit 14937c2

Browse files
René Scharfegitster
authored andcommitted
diff: add option to show whole functions as context
Add the option -W/--function-context to git diff. It is similar to the same option of git grep and expands the context of change hunks so that the whole surrounding function is shown. This "natural" context can allow changes to be understood better. Note: GNU patch doesn't like diffs generated with the new option; it seems to expect context lines to be the same before and after changes. git apply doesn't complain. This implementation has the same shortcoming as the one in grep, namely that there is no way to explicitly find the end of a function. That means that a few lines of extra context are shown, right up to the next recognized function begins. It's already useful in its current form, though. The function get_func_line() in xdiff/xemit.c is extended to work forward as well as backward to find post-context as well as pre-context. It returns the position of the first found matching line. The func_line parameter is made optional, as we don't need it for -W. The enhanced function is then used in xdl_emit_diff() to extend the context as needed. If the added context overlaps with the next change, it is merged into the current hunk. Signed-off-by: Rene Scharfe <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f99f4b3 commit 14937c2

File tree

6 files changed

+154
-6
lines changed

6 files changed

+154
-6
lines changed

Documentation/diff-options.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ endif::git-format-patch[]
404404
Show the context between diff hunks, up to the specified number
405405
of lines, thereby fusing hunks that are close to each other.
406406

407+
-W::
408+
--function-context::
409+
Show whole surrounding functions of changes.
410+
407411
ifndef::git-format-patch[]
408412
--exit-code::
409413
Make the program exit with codes similar to diff(1).

diff.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,8 @@ static void builtin_diff(const char *name_a,
21692169
xecfg.ctxlen = o->context;
21702170
xecfg.interhunkctxlen = o->interhunkcontext;
21712171
xecfg.flags = XDL_EMIT_FUNCNAMES;
2172+
if (DIFF_OPT_TST(o, FUNCCONTEXT))
2173+
xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
21722174
if (pe)
21732175
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
21742176
if (!diffopts)
@@ -3530,6 +3532,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
35303532
else if (opt_arg(arg, '\0', "inter-hunk-context",
35313533
&options->interhunkcontext))
35323534
;
3535+
else if (!strcmp(arg, "-W"))
3536+
DIFF_OPT_SET(options, FUNCCONTEXT);
3537+
else if (!strcmp(arg, "--function-context"))
3538+
DIFF_OPT_SET(options, FUNCCONTEXT);
3539+
else if (!strcmp(arg, "--no-function-context"))
3540+
DIFF_OPT_CLR(options, FUNCCONTEXT);
35333541
else if ((argcount = parse_long_opt("output", av, &optarg))) {
35343542
options->file = fopen(optarg, "w");
35353543
if (!options->file)

diff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
7979
#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
8080
#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
8181
#define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28)
82+
#define DIFF_OPT_FUNCCONTEXT (1 << 29)
8283

8384
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
8485
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)

t/t4051-diff-function-context.sh

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/bin/sh
2+
3+
test_description='diff function context'
4+
5+
. ./test-lib.sh
6+
. "$TEST_DIRECTORY"/diff-lib.sh
7+
8+
9+
cat <<\EOF >hello.c
10+
#include <stdio.h>
11+
12+
static int a(void)
13+
{
14+
/*
15+
* Dummy.
16+
*/
17+
}
18+
19+
static int hello_world(void)
20+
{
21+
/* Classic. */
22+
printf("Hello world.\n");
23+
24+
/* Success! */
25+
return 0;
26+
}
27+
static int b(void)
28+
{
29+
/*
30+
* Dummy, too.
31+
*/
32+
}
33+
34+
int main(int argc, char **argv)
35+
{
36+
a();
37+
b();
38+
return hello_world();
39+
}
40+
EOF
41+
42+
test_expect_success 'setup' '
43+
git add hello.c &&
44+
test_tick &&
45+
git commit -m initial &&
46+
47+
grep -v Classic <hello.c >hello.c.new &&
48+
mv hello.c.new hello.c
49+
'
50+
51+
cat <<\EOF >expected
52+
diff --git a/hello.c b/hello.c
53+
--- a/hello.c
54+
+++ b/hello.c
55+
@@ -10,8 +10,7 @@ static int a(void)
56+
static int hello_world(void)
57+
{
58+
- /* Classic. */
59+
printf("Hello world.\n");
60+
61+
/* Success! */
62+
return 0;
63+
}
64+
EOF
65+
66+
test_expect_success 'diff -U0 -W' '
67+
git diff -U0 -W >actual &&
68+
compare_diff_patch actual expected
69+
'
70+
71+
cat <<\EOF >expected
72+
diff --git a/hello.c b/hello.c
73+
--- a/hello.c
74+
+++ b/hello.c
75+
@@ -9,9 +9,8 @@ static int a(void)
76+
77+
static int hello_world(void)
78+
{
79+
- /* Classic. */
80+
printf("Hello world.\n");
81+
82+
/* Success! */
83+
return 0;
84+
}
85+
EOF
86+
87+
test_expect_success 'diff -W' '
88+
git diff -W >actual &&
89+
compare_diff_patch actual expected
90+
'
91+
92+
test_done

xdiff/xdiff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ extern "C" {
4343

4444
#define XDL_EMIT_FUNCNAMES (1 << 0)
4545
#define XDL_EMIT_COMMON (1 << 1)
46+
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
4647

4748
#define XDL_MMB_READONLY (1 << 0)
4849

xdiff/xemit.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,27 @@ struct func_line {
105105
char buf[80];
106106
};
107107

108-
static void get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
108+
static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
109109
struct func_line *func_line, long start, long limit)
110110
{
111111
find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
112-
long l, size = sizeof(func_line->buf);
113-
char *buf = func_line->buf;
112+
long l, size, step = (start > limit) ? -1 : 1;
113+
char *buf, dummy[1];
114114

115-
for (l = start; l > limit && 0 <= l; l--) {
115+
buf = func_line ? func_line->buf : dummy;
116+
size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
117+
118+
for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
116119
const char *rec;
117120
long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
118121
long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
119122
if (len >= 0) {
120-
func_line->len = len;
121-
break;
123+
if (func_line)
124+
func_line->len = len;
125+
return l;
122126
}
123127
}
128+
return -1;
124129
}
125130

126131
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
@@ -139,13 +144,50 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
139144
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
140145
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
141146

147+
if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
148+
long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
149+
if (fs1 < 0)
150+
fs1 = 0;
151+
if (fs1 < s1) {
152+
s2 -= s1 - fs1;
153+
s1 = fs1;
154+
}
155+
}
156+
157+
again:
142158
lctx = xecfg->ctxlen;
143159
lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
144160
lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
145161

146162
e1 = xche->i1 + xche->chg1 + lctx;
147163
e2 = xche->i2 + xche->chg2 + lctx;
148164

165+
if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
166+
long fe1 = get_func_line(xe, xecfg, NULL,
167+
xche->i1 + xche->chg1,
168+
xe->xdf1.nrec);
169+
if (fe1 < 0)
170+
fe1 = xe->xdf1.nrec;
171+
if (fe1 > e1) {
172+
e2 += fe1 - e1;
173+
e1 = fe1;
174+
}
175+
176+
/*
177+
* Overlap with next change? Then include it
178+
* in the current hunk and start over to find
179+
* its new end.
180+
*/
181+
if (xche->next) {
182+
long l = xche->next->i1;
183+
if (l <= e1 ||
184+
get_func_line(xe, xecfg, NULL, l, e1) < 0) {
185+
xche = xche->next;
186+
goto again;
187+
}
188+
}
189+
}
190+
149191
/*
150192
* Emit current hunk header.
151193
*/

0 commit comments

Comments
 (0)