Skip to content

Commit fda65fa

Browse files
committed
Merge branch 'rs/xdiff-hunk-with-func-line' into maint
"git show -W" (extend hunks to cover the entire function, delimited by lines that match the "funcname" pattern) used to show the entire file when a change added an entire function at the end of the file, which has been fixed. * rs/xdiff-hunk-with-func-line: xdiff: fix merging of appended hunk with -W grep: -W: don't extend context to trailing empty lines t7810: add test for grep -W and trailing empty context lines xdiff: don't trim common tail with -W xdiff: -W: don't include common trailing empty lines in context xdiff: ignore empty lines before added functions with -W xdiff: handle appended chunks better with -W xdiff: factor out match_func_rec() t4051: rewrite, add more tests
2 parents df5a925 + 6f8d9bc commit fda65fa

File tree

10 files changed

+365
-93
lines changed

10 files changed

+365
-93
lines changed

grep.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,9 +1396,17 @@ static int fill_textconv_grep(struct userdiff_driver *driver,
13961396
return 0;
13971397
}
13981398

1399+
static int is_empty_line(const char *bol, const char *eol)
1400+
{
1401+
while (bol < eol && isspace(*bol))
1402+
bol++;
1403+
return bol == eol;
1404+
}
1405+
13991406
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
14001407
{
14011408
char *bol;
1409+
char *peek_bol = NULL;
14021410
unsigned long left;
14031411
unsigned lno = 1;
14041412
unsigned last_hit = 0;
@@ -1543,8 +1551,24 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
15431551
show_function = 1;
15441552
goto next_line;
15451553
}
1546-
if (show_function && match_funcname(opt, gs, bol, eol))
1547-
show_function = 0;
1554+
if (show_function && (!peek_bol || peek_bol < bol)) {
1555+
unsigned long peek_left = left;
1556+
char *peek_eol = eol;
1557+
1558+
/*
1559+
* Trailing empty lines are not interesting.
1560+
* Peek past them to see if they belong to the
1561+
* body of the current function.
1562+
*/
1563+
peek_bol = bol;
1564+
while (is_empty_line(peek_bol, peek_eol)) {
1565+
peek_bol = peek_eol + 1;
1566+
peek_eol = end_of_line(peek_bol, &peek_left);
1567+
}
1568+
1569+
if (match_funcname(opt, gs, peek_bol, peek_eol))
1570+
show_function = 0;
1571+
}
15481572
if (show_function ||
15491573
(last_hit && lno <= last_hit + opt->post_context)) {
15501574
/* If the last hit is within the post context,

t/t4051-diff-function-context.sh

Lines changed: 164 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,90 +3,180 @@
33
test_description='diff function context'
44

55
. ./test-lib.sh
6-
. "$TEST_DIRECTORY"/diff-lib.sh
76

7+
dir="$TEST_DIRECTORY/t4051"
88

9-
cat <<\EOF >hello.c
10-
#include <stdio.h>
11-
12-
static int a(void)
13-
{
14-
/*
15-
* Dummy.
16-
*/
9+
commit_and_tag () {
10+
tag=$1 &&
11+
shift &&
12+
git add "$@" &&
13+
test_tick &&
14+
git commit -m "$tag" &&
15+
git tag "$tag"
1716
}
1817

19-
static int hello_world(void)
20-
{
21-
/* Classic. */
22-
printf("Hello world.\n");
23-
24-
/* Success! */
25-
return 0;
18+
first_context_line () {
19+
awk '
20+
found {print; exit}
21+
/^@@/ {found = 1}
22+
'
2623
}
27-
static int b(void)
28-
{
29-
/*
30-
* Dummy, too.
31-
*/
24+
25+
last_context_line () {
26+
sed -ne \$p
3227
}
3328

34-
int main(int argc, char **argv)
35-
{
36-
a();
37-
b();
38-
return hello_world();
29+
check_diff () {
30+
name=$1
31+
desc=$2
32+
options="-W $3"
33+
34+
test_expect_success "$desc" '
35+
git diff $options "$name^" "$name" >"$name.diff"
36+
'
37+
38+
test_expect_success ' diff applies' '
39+
test_when_finished "git reset --hard" &&
40+
git checkout --detach "$name^" &&
41+
git apply --index "$name.diff" &&
42+
git diff --exit-code "$name"
43+
'
3944
}
40-
EOF
4145

4246
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
47+
cat "$dir/includes.c" "$dir/dummy.c" "$dir/dummy.c" "$dir/hello.c" \
48+
"$dir/dummy.c" "$dir/dummy.c" >file.c &&
49+
commit_and_tag initial file.c &&
50+
51+
grep -v "delete me from hello" <file.c >file.c.new &&
52+
mv file.c.new file.c &&
53+
commit_and_tag changed_hello file.c &&
54+
55+
grep -v "delete me from includes" <file.c >file.c.new &&
56+
mv file.c.new file.c &&
57+
commit_and_tag changed_includes file.c &&
58+
59+
cat "$dir/appended1.c" >>file.c &&
60+
commit_and_tag appended file.c &&
61+
62+
cat "$dir/appended2.c" >>file.c &&
63+
commit_and_tag extended file.c &&
64+
65+
grep -v "Begin of second part" <file.c >file.c.new &&
66+
mv file.c.new file.c &&
67+
commit_and_tag long_common_tail file.c &&
68+
69+
git checkout initial &&
70+
grep -v "delete me from hello" <file.c >file.c.new &&
71+
mv file.c.new file.c &&
72+
cat "$dir/appended1.c" >>file.c &&
73+
commit_and_tag changed_hello_appended file.c
74+
'
75+
76+
check_diff changed_hello 'changed function'
77+
78+
test_expect_success ' context includes begin' '
79+
grep "^ .*Begin of hello" changed_hello.diff
80+
'
81+
82+
test_expect_success ' context includes end' '
83+
grep "^ .*End of hello" changed_hello.diff
84+
'
85+
86+
test_expect_success ' context does not include other functions' '
87+
test $(grep -c "^[ +-].*Begin" changed_hello.diff) -le 1
88+
'
89+
90+
test_expect_success ' context does not include preceding empty lines' '
91+
test "$(first_context_line <changed_hello.diff)" != " "
92+
'
93+
94+
test_expect_success ' context does not include trailing empty lines' '
95+
test "$(last_context_line <changed_hello.diff)" != " "
96+
'
97+
98+
check_diff changed_includes 'changed includes'
99+
100+
test_expect_success ' context includes begin' '
101+
grep "^ .*Begin.h" changed_includes.diff
102+
'
103+
104+
test_expect_success ' context includes end' '
105+
grep "^ .*End.h" changed_includes.diff
106+
'
107+
108+
test_expect_success ' context does not include other functions' '
109+
test $(grep -c "^[ +-].*Begin" changed_includes.diff) -le 1
110+
'
111+
112+
test_expect_success ' context does not include trailing empty lines' '
113+
test "$(last_context_line <changed_includes.diff)" != " "
114+
'
115+
116+
check_diff appended 'appended function'
117+
118+
test_expect_success ' context includes begin' '
119+
grep "^[+].*Begin of first part" appended.diff
120+
'
121+
122+
test_expect_success ' context includes end' '
123+
grep "^[+].*End of first part" appended.diff
124+
'
125+
126+
test_expect_success ' context does not include other functions' '
127+
test $(grep -c "^[ +-].*Begin" appended.diff) -le 1
128+
'
129+
130+
check_diff extended 'appended function part'
131+
132+
test_expect_success ' context includes begin' '
133+
grep "^ .*Begin of first part" extended.diff
134+
'
135+
136+
test_expect_success ' context includes end' '
137+
grep "^[+].*End of second part" extended.diff
138+
'
139+
140+
test_expect_success ' context does not include other functions' '
141+
test $(grep -c "^[ +-].*Begin" extended.diff) -le 2
142+
'
143+
144+
test_expect_success ' context does not include preceding empty lines' '
145+
test "$(first_context_line <extended.diff)" != " "
146+
'
147+
148+
check_diff long_common_tail 'change with long common tail and no context' -U0
149+
150+
test_expect_success ' context includes begin' '
151+
grep "^ .*Begin of first part" long_common_tail.diff
152+
'
153+
154+
test_expect_success ' context includes end' '
155+
grep "^ .*End of second part" long_common_tail.diff
156+
'
157+
158+
test_expect_success ' context does not include other functions' '
159+
test $(grep -c "^[ +-].*Begin" long_common_tail.diff) -le 2
160+
'
161+
162+
test_expect_success ' context does not include preceding empty lines' '
163+
test "$(first_context_line <long_common_tail.diff.diff)" != " "
164+
'
165+
166+
check_diff changed_hello_appended 'changed function plus appended function'
167+
168+
test_expect_success ' context includes begin' '
169+
grep "^ .*Begin of hello" changed_hello_appended.diff &&
170+
grep "^[+].*Begin of first part" changed_hello_appended.diff
171+
'
172+
173+
test_expect_success ' context includes end' '
174+
grep "^ .*End of hello" changed_hello_appended.diff &&
175+
grep "^[+].*End of first part" changed_hello_appended.diff
176+
'
177+
178+
test_expect_success ' context does not include other functions' '
179+
test $(grep -c "^[ +-].*Begin" changed_hello_appended.diff) -le 2
90180
'
91181

92182
test_done

t/t4051/appended1.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
int appended(void) // Begin of first part
3+
{
4+
int i;
5+
char *s = "a string";
6+
7+
printf("%s\n", s);
8+
9+
for (i = 99;
10+
i >= 0;
11+
i--) {
12+
printf("%d bottles of beer on the wall\n", i);
13+
}
14+
15+
printf("End of first part\n");

t/t4051/appended2.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
printf("Begin of second part\n");
2+
3+
/*
4+
* Lorem ipsum dolor sit amet, consectetuer sadipscing elitr,
5+
* sed diam nonumy eirmod tempor invidunt ut labore et dolore
6+
* magna aliquyam erat, sed diam voluptua. At vero eos et
7+
* accusam et justo duo dolores et ea rebum. Stet clita kasd
8+
* gubergren, no sea takimata sanctus est Lorem ipsum dolor
9+
* sit amet.
10+
*
11+
* Lorem ipsum dolor sit amet, consectetuer sadipscing elitr,
12+
* sed diam nonumy eirmod tempor invidunt ut labore et dolore
13+
* magna aliquyam erat, sed diam voluptua. At vero eos et
14+
* accusam et justo duo dolores et ea rebum. Stet clita kasd
15+
* gubergren, no sea takimata sanctus est Lorem ipsum dolor
16+
* sit amet.
17+
*
18+
* Lorem ipsum dolor sit amet, consectetuer sadipscing elitr,
19+
* sed diam nonumy eirmod tempor invidunt ut labore et dolore
20+
* magna aliquyam erat, sed diam voluptua. At vero eos et
21+
* accusam et justo duo dolores et ea rebum. Stet clita kasd
22+
* gubergren, no sea takimata sanctus est Lorem ipsum dolor
23+
* sit amet.
24+
*
25+
* Lorem ipsum dolor sit amet, consectetuer sadipscing elitr,
26+
* sed diam nonumy eirmod tempor invidunt ut labore et dolore
27+
* magna aliquyam erat, sed diam voluptua. At vero eos et
28+
* accusam et justo duo dolores et ea rebum. Stet clita kasd
29+
* gubergren, no sea takimata sanctus est Lorem ipsum dolor
30+
* sit amet.
31+
*
32+
*/
33+
34+
return 0;
35+
} // End of second part

t/t4051/dummy.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
static int dummy(void) // Begin of dummy
3+
{
4+
int rc = 0;
5+
6+
return rc;
7+
} // End of dummy

t/t4051/hello.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
static void hello(void) // Begin of hello
3+
{
4+
/*
5+
* Classic.
6+
*/
7+
putchar('H');
8+
putchar('e');
9+
putchar('l');
10+
putchar('l');
11+
putchar('o');
12+
putchar(' ');
13+
/* delete me from hello */
14+
putchar('w');
15+
putchar('o');
16+
putchar('r');
17+
putchar('l');
18+
putchar('d');
19+
putchar('.');
20+
putchar('\n');
21+
} // End of hello

0 commit comments

Comments
 (0)