line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::GitHooks::Plugin::PreventTrailingWhitespace; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
25373542
|
use strict; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
384
|
|
4
|
11
|
|
|
11
|
|
57
|
use warnings; |
|
11
|
|
|
|
|
16
|
|
|
11
|
|
|
|
|
330
|
|
5
|
|
|
|
|
|
|
|
6
|
11
|
|
|
11
|
|
58
|
use base 'App::GitHooks::Plugin'; |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
3642
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# External dependencies. |
9
|
11
|
|
|
11
|
|
4225
|
use File::Slurp; |
|
11
|
|
|
|
|
51309
|
|
|
11
|
|
|
|
|
931
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Internal dependencies. |
12
|
11
|
|
|
11
|
|
3064
|
use App::GitHooks::Constants qw( :PLUGIN_RETURN_CODES ); |
|
11
|
|
|
|
|
1021
|
|
|
11
|
|
|
|
|
8686
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
App::GitHooks::Plugin::PreventTrailingWhitespace - Prevent trailing whitespace from being committed. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 DESCRIPTION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Prevent pesky trailing whitespace from being committed. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 VERSION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Version 1.0.1 |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=cut |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our $VERSION = '1.0.1'; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 METHODS |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head2 get_file_pattern() |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Return a pattern to filter the files this plugin should analyze. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $file_pattern = App::GitHooks::Plugin::PreventTrailingWhitespace->get_file_pattern( |
40
|
|
|
|
|
|
|
app => $app, |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub get_file_pattern |
46
|
|
|
|
|
|
|
{ |
47
|
9
|
|
|
9
|
1
|
19440370
|
return qr/\.(?:pl|pm|t|cgi|js|tt|css|html|rb)$/x; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head2 get_file_check_description() |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Return a description of the check performed on files by the plugin and that |
54
|
|
|
|
|
|
|
will be displayed to the user, if applicable, along with an indication of the |
55
|
|
|
|
|
|
|
success or failure of the plugin. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my $description = App::GitHooks::Plugin::PreventTrailingWhitespace->get_file_check_description(); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=cut |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub get_file_check_description |
62
|
|
|
|
|
|
|
{ |
63
|
9
|
|
|
9
|
1
|
9086
|
return 'The file has no lines with trailing white space.'; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 run_pre_commit_file() |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Code to execute for each file as part of the pre-commit hook. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
This is where the magic happens. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
my $success = App::GitHooks::Plugin::PreventTrailingWhitespace->run_pre_commit_file(); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=cut |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub run_pre_commit_file |
78
|
|
|
|
|
|
|
{ |
79
|
4
|
|
|
4
|
1
|
10073
|
my ( $class, %args ) = @_; |
80
|
4
|
|
|
|
|
282
|
my $file = delete( $args{'file'} ); |
81
|
4
|
|
|
|
|
92
|
my $git_action = delete( $args{'git_action'} ); |
82
|
4
|
|
|
|
|
71
|
my $app = delete( $args{'app'} ); |
83
|
4
|
|
|
|
|
285
|
my $staged_changes = $app->get_staged_changes(); |
84
|
4
|
|
|
|
|
295
|
my $repository = $app->get_repository(); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Ignore deleted files. |
87
|
4
|
50
|
|
|
|
202
|
return $PLUGIN_RETURN_SKIPPED |
88
|
|
|
|
|
|
|
if $git_action eq 'D'; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# Ignore merges, since they correspond mostly to code written by other people. |
91
|
4
|
50
|
|
|
|
176
|
return $PLUGIN_RETURN_SKIPPED |
92
|
|
|
|
|
|
|
if $staged_changes->is_merge(); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Ignore revert commits. |
95
|
4
|
50
|
|
|
|
663
|
return $PLUGIN_RETURN_SKIPPED |
96
|
|
|
|
|
|
|
if $staged_changes->is_revert(); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Determine what lines were written by the current user. |
99
|
4
|
|
|
|
|
87
|
my @review_lines = (); |
100
|
4
|
50
|
|
|
|
54
|
if ( $git_action eq 'A' ) |
101
|
|
|
|
|
|
|
{ |
102
|
|
|
|
|
|
|
# "git blame" fails on new files, so we need to add the entire file |
103
|
|
|
|
|
|
|
# separately. |
104
|
4
|
|
|
|
|
179
|
my @lines = File::Slurp::read_file( $file ); |
105
|
4
|
|
|
|
|
4836
|
foreach my $i ( 0 .. scalar( @lines ) - 1 ) |
106
|
|
|
|
|
|
|
{ |
107
|
17
|
|
|
|
|
64
|
chomp( $lines[$i] ); |
108
|
17
|
|
|
|
|
109
|
push( |
109
|
|
|
|
|
|
|
@review_lines, |
110
|
|
|
|
|
|
|
{ |
111
|
|
|
|
|
|
|
line_number => $i, |
112
|
|
|
|
|
|
|
code => $lines[$i], |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
else |
118
|
|
|
|
|
|
|
{ |
119
|
|
|
|
|
|
|
# Find uncommitted lines only. |
120
|
0
|
|
|
|
|
0
|
my $blame_lines = $repository->blame( |
121
|
|
|
|
|
|
|
$file, |
122
|
|
|
|
|
|
|
use_cache => 1, |
123
|
|
|
|
|
|
|
); |
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
foreach my $blame_line ( @$blame_lines ) |
126
|
|
|
|
|
|
|
{ |
127
|
0
|
|
|
|
|
0
|
my $commit_attributes = $blame_line->get_commit_attributes(); |
128
|
0
|
0
|
|
|
|
0
|
next unless $commit_attributes->{'author-mail'} eq 'not.committed.yet'; |
129
|
0
|
|
|
|
|
0
|
push( |
130
|
|
|
|
|
|
|
@review_lines, |
131
|
|
|
|
|
|
|
{ |
132
|
|
|
|
|
|
|
line_number => $blame_line->get_line_number(), |
133
|
|
|
|
|
|
|
code => $blame_line->get_line(), |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
); |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Inspect uncommitted lines for trailing white space |
140
|
4
|
|
|
|
|
19
|
my @whitespace_violations = (); |
141
|
4
|
|
|
|
|
27
|
foreach my $line ( @review_lines ) |
142
|
|
|
|
|
|
|
{ |
143
|
17
|
|
|
|
|
46
|
my $code = $line->{'code'}; |
144
|
17
|
|
|
|
|
158
|
my ( $trailing_whitespace ) = $code =~ m/(\s+)$/; |
145
|
17
|
100
|
|
|
|
82
|
next if !defined( $trailing_whitespace ); |
146
|
|
|
|
|
|
|
|
147
|
3
|
|
|
|
|
128
|
my $redspace = ''; |
148
|
3
|
|
|
|
|
65
|
$redspace .= $app->color('on_red', $_) foreach (split //, $trailing_whitespace); |
149
|
3
|
|
|
|
|
1141
|
( my $badline = $code ) =~ s/\s+$/$redspace/; |
150
|
3
|
|
|
|
|
78
|
push( |
151
|
|
|
|
|
|
|
@whitespace_violations, |
152
|
|
|
|
|
|
|
sprintf( "line #%d: %s\n", $line->{'line_number'}, $badline ) |
153
|
|
|
|
|
|
|
); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
4
|
100
|
|
|
|
158
|
die "Trailing white space found:\n" . join( '', @whitespace_violations ) . "\n" |
157
|
|
|
|
|
|
|
if scalar( @whitespace_violations ) != 0; |
158
|
|
|
|
|
|
|
|
159
|
1
|
|
|
|
|
9
|
return $PLUGIN_RETURN_PASSED; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 BUGS |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
165
|
|
|
|
|
|
|
L. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head1 SUPPORT |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
perldoc App::GitHooks::Plugin::PreventTrailingWhitespace |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
You can also look for information at: |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=over |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item * GitHub's request tracker |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
L |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
L |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item * CPAN Ratings |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
L |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item * MetaCPAN |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
L |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=back |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head1 AUTHOR |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
L, |
201
|
|
|
|
|
|
|
C<< >>. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
For guidance on this module and for creating App::GitHooks, big thanks to: |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
L, |
208
|
|
|
|
|
|
|
C<< >>. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Copyright 2013-2014 Ben Arwin. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify it under |
215
|
|
|
|
|
|
|
the terms of the GNU General Public License version 3 as published by the Free |
216
|
|
|
|
|
|
|
Software Foundation. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY |
219
|
|
|
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
220
|
|
|
|
|
|
|
PARTICULAR PURPOSE. See the GNU General Public License for more details. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with |
223
|
|
|
|
|
|
|
this program. If not, see http://www.gnu.org/licenses/ |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
1; |