diff options
| -rw-r--r-- | builtin-apply.c | 68 | ||||
| -rwxr-xr-x | t/t4124-apply-ws-rule.sh | 133 | 
2 files changed, 182 insertions, 19 deletions
| diff --git a/builtin-apply.c b/builtin-apply.c index eb09bfef42..e04b493888 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -910,23 +910,35 @@ static void check_whitespace(const char *line, int len)  	 * this function.  That is, an addition of an empty line would  	 * check the '+' here.  Sneaky...  	 */ -	if (isspace(line[len-2])) +	if ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))  		goto error;  	/*  	 * Make sure that there is no space followed by a tab in  	 * indentation.  	 */ -	err = "Space in indent is followed by a tab"; -	for (i = 1; i < len; i++) { -		if (line[i] == '\t') { -			if (seen_space) -				goto error; -		} -		else if (line[i] == ' ') -			seen_space = 1; -		else -			break; +	if (whitespace_rule & WS_SPACE_BEFORE_TAB) { +		err = "Space in indent is followed by a tab"; +		for (i = 1; i < len; i++) { +			if (line[i] == '\t') { +				if (seen_space) +					goto error; +			} +			else if (line[i] == ' ') +				seen_space = 1; +			else +				break; +		} +	} + +	/* +	 * Make sure that the indentation does not contain more than +	 * 8 spaces. +	 */ +	if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && +	    (8 < len) && !strncmp("+        ", line, 9)) { +		err = "Indent more than 8 places with spaces"; +		goto error;  	}  	return; @@ -1581,7 +1593,8 @@ static int apply_line(char *output, const char *patch, int plen)  	/*  	 * Strip trailing whitespace  	 */ -	if (1 < plen && isspace(patch[plen-1])) { +	if ((whitespace_rule & WS_TRAILING_SPACE) && +	    (1 < plen && isspace(patch[plen-1]))) {  		if (patch[plen] == '\n')  			add_nl_to_tail = 1;  		plen--; @@ -1597,11 +1610,16 @@ static int apply_line(char *output, const char *patch, int plen)  		char ch = patch[i];  		if (ch == '\t') {  			last_tab_in_indent = i; -			if (0 <= last_space_in_indent) +			if ((whitespace_rule & WS_SPACE_BEFORE_TAB) && +			    0 <= last_space_in_indent) +			    need_fix_leading_space = 1; +		} else if (ch == ' ') { +			last_space_in_indent = i; +			if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && +			    last_tab_in_indent < 0 && +			    8 <= i)  				need_fix_leading_space = 1;  		} -		else if (ch == ' ') -			last_space_in_indent = i;  		else  			break;  	} @@ -1609,11 +1627,21 @@ static int apply_line(char *output, const char *patch, int plen)  	buf = output;  	if (need_fix_leading_space) {  		int consecutive_spaces = 0; +		int last = last_tab_in_indent + 1; + +		if (whitespace_rule & WS_INDENT_WITH_NON_TAB) { +			/* have "last" point at one past the indent */ +			if (last_tab_in_indent < last_space_in_indent) +				last = last_space_in_indent + 1; +			else +				last = last_tab_in_indent + 1; +		} +  		/* -		 * between patch[1..last_tab_in_indent] strip the -		 * funny spaces, updating them to tab as needed. +		 * between patch[1..last], strip the funny spaces, +		 * updating them to tab as needed.  		 */ -		for (i = 1; i < last_tab_in_indent; i++, plen--) { +		for (i = 1; i < last; i++, plen--) {  			char ch = patch[i];  			if (ch != ' ') {  				consecutive_spaces = 0; @@ -1626,8 +1654,10 @@ static int apply_line(char *output, const char *patch, int plen)  				}  			}  		} +		while (0 < consecutive_spaces--) +			*output++ = ' ';  		fixed = 1; -		i = last_tab_in_indent; +		i = last;  	}  	else  		i = 1; diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh new file mode 100755 index 0000000000..f53ac466d6 --- /dev/null +++ b/t/t4124-apply-ws-rule.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +test_description='core.whitespace rules and git-apply' + +. ./test-lib.sh + +prepare_test_file () { + +	# A line that has character X is touched iff RULE is in effect: +	#       X  RULE +	#   	!  trailing-space +	#   	@  space-before-tab +	#   	#  indent-with-non-tab +	sed -e "s/_/ /g" -e "s/>/	/" <<-\EOF +		An_SP in an ordinary line>and a HT. +		>A HT. +		_>A SP and a HT (@). +		_>_A SP, a HT and a SP (@). +		_______Seven SP. +		________Eight SP (#). +		_______>Seven SP and a HT (@). +		________>Eight SP and a HT (@#). +		_______>_Seven SP, a HT and a SP (@). +		________>_Eight SP, a HT and a SP (@#). +		_______________Fifteen SP (#). +		_______________>Fifteen SP and a HT (@#). +		________________Sixteen SP (#). +		________________>Sixteen SP and a HT (@#). +		_____a__Five SP, a non WS, two SP. +		A line with a (!) trailing SP_ +		A line with a (!) trailing HT> +	EOF +} + +apply_patch () { +	>target && +	sed -e "s|\([ab]\)/file|\1/target|" <patch | +	git apply "$@" +} + +test_fix () { + +	# fix should not barf +	apply_patch --whitespace=fix || return 1 + +	# find touched lines +	diff file target | sed -n -e "s/^> //p" >fixed + +	# the changed lines are all expeced to change +	fixed_cnt=$(wc -l <fixed) +	case "$1" in +	'') expect_cnt=$fixed_cnt ;; +	?*) expect_cnt=$(grep "[$1]" <fixed | wc -l) ;; +	esac +	test $fixed_cnt -eq $expect_cnt || return 1 + +	# and we are not missing anything +	case "$1" in +	'') expect_cnt=0 ;; +	?*) expect_cnt=$(grep "[$1]" <file | wc -l) ;; +	esac +	test $fixed_cnt -eq $expect_cnt || return 1 + +	# Get the patch actually applied +	git diff-files -p target >fixed-patch +	test -s fixed-patch && return 0 + +	# Make sure it is complaint-free +	>target +	git apply --whitespace=error-all <fixed-patch + +} + +test_expect_success setup ' + +	>file && +	git add file && +	prepare_test_file >file && +	git diff-files -p >patch && +	>target && +	git add target + +' + +test_expect_success 'whitespace=nowarn, default rule' ' + +	apply_patch --whitespace=nowarn && +	diff file target + +' + +test_expect_success 'whitespace=warn, default rule' ' + +	apply_patch --whitespace=warn && +	diff file target + +' + +test_expect_success 'whitespace=error-all, default rule' ' + +	apply_patch --whitespace=error-all && return 1 +	test -s target && return 1 +	: happy + +' + +test_expect_success 'whitespace=error-all, no rule' ' + +	git config core.whitespace -trailing,-space-before,-indent && +	apply_patch --whitespace=error-all && +	diff file target + +' + +for t in - '' +do +	case "$t" in '') tt='!' ;; *) tt= ;; esac +	for s in - '' +	do +		case "$s" in '') ts='@' ;; *) ts= ;; esac +		for i in - '' +		do +			case "$i" in '') ti='#' ;; *) ti= ;; esac +			rule=${t}trailing,${s}space,${i}indent && +			test_expect_success "rule=$rule" ' +				git config core.whitespace "$rule" && +				test_fix "$tt$ts$ti" +			' +		done +	done +done + +test_done | 
