aboutsummaryrefslogtreecommitdiffstats
path: root/commit-check
blob: cfdc0bfcd973911e3dd7fcb19316cb8270206165 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env perl

## commit-check --- Simple git commit style checker
# Copyright (C) 2014  Tom Willemse <tom@ryuslash.org>

# commit-check is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# commit-check is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with commit-ccheck. If not, see <http://www.gnu.org/licenses/>.

=head1 Commit check

This is a simple commit style checker. It is made to be used as a git
C<commit-msg> hook. It expects the name of the file to check as the
first command-line argument. The style that's checked is based on
tpope's L<A Note About Git Commit
Messages|http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>.

=head2 Command-line options

There are 2 command line options available for use with this program:

=over

=item -0

Don't return a non-zero status when errors have been found. For use as
a git C<commit-msg> hook it is important that it does exit with a
non-zero status. But if you are using it for other tools, such as for
a flycheck checker, exiting with a non-zero status might confuse the
tools calling it.

=item -h

Show a little help text that describes the command line options and
usage.

=back

When no arguments are passed (not even the file name) the help text is
also shown and the exit status is non-0 (no matter if C<-0> has been
given).

=head2 Errors

Currently there are 4 style errors that are checked for.

=over

=item 1

The first character of the first line of the commit message should be
capitalized.

=item 2

The first line of the commit message should be no longer than 50
characters.

=item 3

The second line of the commit message should be empty.

=item 4

No line should be longer than 72 characters.

=back

All comments are skipped and so is all whitespace at the beginning of
the file.

If any of the error conditions are encountered, and the C<-0> option
has not been given, the program will exit with a non-C<0> status. This
has the effect of stopping git from committing the message when this
program is used as a C<commit-msg> hook. Each error condition
encountered also prints a message to the standard error stream.

=cut

use strict;
use warnings;
use File::Basename;
use Getopt::Std;

my $lineno = 0;
my $status = 0;
my %arguments = ();

sub err {
    my ($msg) = @_;

    print STDERR basename($0) .":". $ARGV[0] .":$.: ". $msg ."\n";
    $status = 1;
}

sub usage {
    my ($status) = @_;
    print "Usage: commit-check [-0|-h] <file>\n"
        . "\n"
        . "Accepted arguments:\n"
        . "  -0   Always return 0 exit-status, regardless of errors found.\n"
        . "  -h   Show this help text.\n";
    exit $status;
}

getopts("0h", \%arguments);

usage 0 if $arguments{h};
usage 1 if !$ARGV[0];

open(my $commitfile, "<", $ARGV[0]) or die "Couldn't open $ARGV[0]";

while (<$commitfile>) {
    next if /^#/;                 # Discard comments
    next if $lineno == 0 && /^$/; # Discard leading empty lines
    $lineno++;                    # Start at 1, so increment first

    if ($lineno == 1) {
        err "First line is not capitalized" if /^[^[:upper:]]/;
        err "First line is longer than 50 characters" if /^.{51,}/;
        next;
    }

    err "Second line should be empty" if $lineno == 2 && /.+/;
    err "Line is longer than 72 characters" if /^.{73,}/;
}

close $commitfile;
exit $status if !$arguments{0};
exit 0;