Skip to content

Commit d5a4164

Browse files
committed
builtin-apply: teach whitespace_rules
We earlier introduced core.whitespace to allow users to tweak the definition of what the "whitespace errors" are, for the purpose of diff output highlighting. This teaches the same to git-apply, so that the command can both detect (when --whitespace=warn option is given) and fix (when --whitespace=fix option is given) as configured. Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 81bf96b commit d5a4164

File tree

2 files changed

+182
-19
lines changed

2 files changed

+182
-19
lines changed

builtin-apply.c

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -910,23 +910,35 @@ static void check_whitespace(const char *line, int len)
910910
* this function. That is, an addition of an empty line would
911911
* check the '+' here. Sneaky...
912912
*/
913-
if (isspace(line[len-2]))
913+
if ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))
914914
goto error;
915915

916916
/*
917917
* Make sure that there is no space followed by a tab in
918918
* indentation.
919919
*/
920-
err = "Space in indent is followed by a tab";
921-
for (i = 1; i < len; i++) {
922-
if (line[i] == '\t') {
923-
if (seen_space)
924-
goto error;
925-
}
926-
else if (line[i] == ' ')
927-
seen_space = 1;
928-
else
929-
break;
920+
if (whitespace_rule & WS_SPACE_BEFORE_TAB) {
921+
err = "Space in indent is followed by a tab";
922+
for (i = 1; i < len; i++) {
923+
if (line[i] == '\t') {
924+
if (seen_space)
925+
goto error;
926+
}
927+
else if (line[i] == ' ')
928+
seen_space = 1;
929+
else
930+
break;
931+
}
932+
}
933+
934+
/*
935+
* Make sure that the indentation does not contain more than
936+
* 8 spaces.
937+
*/
938+
if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&
939+
(8 < len) && !strncmp("+ ", line, 9)) {
940+
err = "Indent more than 8 places with spaces";
941+
goto error;
930942
}
931943
return;
932944

@@ -1581,7 +1593,8 @@ static int apply_line(char *output, const char *patch, int plen)
15811593
/*
15821594
* Strip trailing whitespace
15831595
*/
1584-
if (1 < plen && isspace(patch[plen-1])) {
1596+
if ((whitespace_rule & WS_TRAILING_SPACE) &&
1597+
(1 < plen && isspace(patch[plen-1]))) {
15851598
if (patch[plen] == '\n')
15861599
add_nl_to_tail = 1;
15871600
plen--;
@@ -1597,23 +1610,38 @@ static int apply_line(char *output, const char *patch, int plen)
15971610
char ch = patch[i];
15981611
if (ch == '\t') {
15991612
last_tab_in_indent = i;
1600-
if (0 <= last_space_in_indent)
1613+
if ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&
1614+
0 <= last_space_in_indent)
1615+
need_fix_leading_space = 1;
1616+
} else if (ch == ' ') {
1617+
last_space_in_indent = i;
1618+
if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&
1619+
last_tab_in_indent < 0 &&
1620+
8 <= i)
16011621
need_fix_leading_space = 1;
16021622
}
1603-
else if (ch == ' ')
1604-
last_space_in_indent = i;
16051623
else
16061624
break;
16071625
}
16081626

16091627
buf = output;
16101628
if (need_fix_leading_space) {
16111629
int consecutive_spaces = 0;
1630+
int last = last_tab_in_indent + 1;
1631+
1632+
if (whitespace_rule & WS_INDENT_WITH_NON_TAB) {
1633+
/* have "last" point at one past the indent */
1634+
if (last_tab_in_indent < last_space_in_indent)
1635+
last = last_space_in_indent + 1;
1636+
else
1637+
last = last_tab_in_indent + 1;
1638+
}
1639+
16121640
/*
1613-
* between patch[1..last_tab_in_indent] strip the
1614-
* funny spaces, updating them to tab as needed.
1641+
* between patch[1..last], strip the funny spaces,
1642+
* updating them to tab as needed.
16151643
*/
1616-
for (i = 1; i < last_tab_in_indent; i++, plen--) {
1644+
for (i = 1; i < last; i++, plen--) {
16171645
char ch = patch[i];
16181646
if (ch != ' ') {
16191647
consecutive_spaces = 0;
@@ -1626,8 +1654,10 @@ static int apply_line(char *output, const char *patch, int plen)
16261654
}
16271655
}
16281656
}
1657+
while (0 < consecutive_spaces--)
1658+
*output++ = ' ';
16291659
fixed = 1;
1630-
i = last_tab_in_indent;
1660+
i = last;
16311661
}
16321662
else
16331663
i = 1;

t/t4124-apply-ws-rule.sh

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/bin/sh
2+
3+
test_description='core.whitespace rules and git-apply'
4+
5+
. ./test-lib.sh
6+
7+
prepare_test_file () {
8+
9+
# A line that has character X is touched iff RULE is in effect:
10+
# X RULE
11+
# ! trailing-space
12+
# @ space-before-tab
13+
# # indent-with-non-tab
14+
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
15+
An_SP in an ordinary line>and a HT.
16+
>A HT.
17+
_>A SP and a HT (@).
18+
_>_A SP, a HT and a SP (@).
19+
_______Seven SP.
20+
________Eight SP (#).
21+
_______>Seven SP and a HT (@).
22+
________>Eight SP and a HT (@#).
23+
_______>_Seven SP, a HT and a SP (@).
24+
________>_Eight SP, a HT and a SP (@#).
25+
_______________Fifteen SP (#).
26+
_______________>Fifteen SP and a HT (@#).
27+
________________Sixteen SP (#).
28+
________________>Sixteen SP and a HT (@#).
29+
_____a__Five SP, a non WS, two SP.
30+
A line with a (!) trailing SP_
31+
A line with a (!) trailing HT>
32+
EOF
33+
}
34+
35+
apply_patch () {
36+
>target &&
37+
sed -e "s|\([ab]\)/file|\1/target|" <patch |
38+
git apply "$@"
39+
}
40+
41+
test_fix () {
42+
43+
# fix should not barf
44+
apply_patch --whitespace=fix || return 1
45+
46+
# find touched lines
47+
diff file target | sed -n -e "s/^> //p" >fixed
48+
49+
# the changed lines are all expeced to change
50+
fixed_cnt=$(wc -l <fixed)
51+
case "$1" in
52+
'') expect_cnt=$fixed_cnt ;;
53+
?*) expect_cnt=$(grep "[$1]" <fixed | wc -l) ;;
54+
esac
55+
test $fixed_cnt -eq $expect_cnt || return 1
56+
57+
# and we are not missing anything
58+
case "$1" in
59+
'') expect_cnt=0 ;;
60+
?*) expect_cnt=$(grep "[$1]" <file | wc -l) ;;
61+
esac
62+
test $fixed_cnt -eq $expect_cnt || return 1
63+
64+
# Get the patch actually applied
65+
git diff-files -p target >fixed-patch
66+
test -s fixed-patch && return 0
67+
68+
# Make sure it is complaint-free
69+
>target
70+
git apply --whitespace=error-all <fixed-patch
71+
72+
}
73+
74+
test_expect_success setup '
75+
76+
>file &&
77+
git add file &&
78+
prepare_test_file >file &&
79+
git diff-files -p >patch &&
80+
>target &&
81+
git add target
82+
83+
'
84+
85+
test_expect_success 'whitespace=nowarn, default rule' '
86+
87+
apply_patch --whitespace=nowarn &&
88+
diff file target
89+
90+
'
91+
92+
test_expect_success 'whitespace=warn, default rule' '
93+
94+
apply_patch --whitespace=warn &&
95+
diff file target
96+
97+
'
98+
99+
test_expect_success 'whitespace=error-all, default rule' '
100+
101+
apply_patch --whitespace=error-all && return 1
102+
test -s target && return 1
103+
: happy
104+
105+
'
106+
107+
test_expect_success 'whitespace=error-all, no rule' '
108+
109+
git config core.whitespace -trailing,-space-before,-indent &&
110+
apply_patch --whitespace=error-all &&
111+
diff file target
112+
113+
'
114+
115+
for t in - ''
116+
do
117+
case "$t" in '') tt='!' ;; *) tt= ;; esac
118+
for s in - ''
119+
do
120+
case "$s" in '') ts='@' ;; *) ts= ;; esac
121+
for i in - ''
122+
do
123+
case "$i" in '') ti='#' ;; *) ti= ;; esac
124+
rule=${t}trailing,${s}space,${i}indent &&
125+
test_expect_success "rule=$rule" '
126+
git config core.whitespace "$rule" &&
127+
test_fix "$tt$ts$ti"
128+
'
129+
done
130+
done
131+
done
132+
133+
test_done

0 commit comments

Comments
 (0)