Skip to content

Commit 9b55aa0

Browse files
committed
Merge branch 'rs/diff-whole-function'
* rs/diff-whole-function: diff: add option to show whole functions as context xdiff: factor out get_func_line()
2 parents 662384c + 14937c2 commit 9b55aa0

File tree

6 files changed

+175
-16
lines changed

6 files changed

+175
-16
lines changed

Documentation/diff-options.txt

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

411+
-W::
412+
--function-context::
413+
Show whole surrounding functions of changes.
414+
411415
ifndef::git-format-patch[]
412416
--exit-code::
413417
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)
@@ -3536,6 +3538,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
35363538
else if (opt_arg(arg, '\0', "inter-hunk-context",
35373539
&options->interhunkcontext))
35383540
;
3541+
else if (!strcmp(arg, "-W"))
3542+
DIFF_OPT_SET(options, FUNCCONTEXT);
3543+
else if (!strcmp(arg, "--function-context"))
3544+
DIFF_OPT_SET(options, FUNCCONTEXT);
3545+
else if (!strcmp(arg, "--no-function-context"))
3546+
DIFF_OPT_CLR(options, FUNCCONTEXT);
35393547
else if ((argcount = parse_long_opt("output", av, &optarg))) {
35403548
options->file = fopen(optarg, "w");
35413549
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: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,40 @@ static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
100100
return 0;
101101
}
102102

103+
struct func_line {
104+
long len;
105+
char buf[80];
106+
};
107+
108+
static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
109+
struct func_line *func_line, long start, long limit)
110+
{
111+
find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
112+
long l, size, step = (start > limit) ? -1 : 1;
113+
char *buf, dummy[1];
114+
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) {
119+
const char *rec;
120+
long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
121+
long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
122+
if (len >= 0) {
123+
if (func_line)
124+
func_line->len = len;
125+
return l;
126+
}
127+
}
128+
return -1;
129+
}
130+
103131
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
104132
xdemitconf_t const *xecfg) {
105133
long s1, s2, e1, e2, lctx;
106134
xdchange_t *xch, *xche;
107-
char funcbuf[80];
108-
long funclen = 0;
109135
long funclineprev = -1;
110-
find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
136+
struct func_line func_line = { 0 };
111137

112138
if (xecfg->flags & XDL_EMIT_COMMON)
113139
return xdl_emit_common(xe, xscr, ecb, xecfg);
@@ -118,34 +144,61 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
118144
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
119145
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
120146

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:
121158
lctx = xecfg->ctxlen;
122159
lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
123160
lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
124161

125162
e1 = xche->i1 + xche->chg1 + lctx;
126163
e2 = xche->i2 + xche->chg2 + lctx;
127164

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+
128191
/*
129192
* Emit current hunk header.
130193
*/
131194

132195
if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
133-
long l;
134-
for (l = s1 - 1; l >= 0 && l > funclineprev; l--) {
135-
const char *rec;
136-
long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
137-
long newfunclen = ff(rec, reclen, funcbuf,
138-
sizeof(funcbuf),
139-
xecfg->find_func_priv);
140-
if (newfunclen >= 0) {
141-
funclen = newfunclen;
142-
break;
143-
}
144-
}
196+
get_func_line(xe, xecfg, &func_line,
197+
s1 - 1, funclineprev);
145198
funclineprev = s1 - 1;
146199
}
147200
if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
148-
funcbuf, funclen, ecb) < 0)
201+
func_line.buf, func_line.len, ecb) < 0)
149202
return -1;
150203

151204
/*

0 commit comments

Comments
 (0)