Skip to content

Commit 20b1649

Browse files
committed
Initial commit - v0.1.0
0 parents  commit 20b1649

File tree

11 files changed

+714
-0
lines changed

11 files changed

+714
-0
lines changed

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file is for unifying the coding style for different editors and IDEs
2+
# editorconfig.org
3+
4+
root = true
5+
6+
[*]
7+
charset = utf-8
8+
end_of_line = lf
9+
insert_final_newline = true
10+
trim_trailing_whitespace = true
11+
indent_style = space
12+
indent_size = 2
13+
14+
[Makefile]
15+
indent_style = tab

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor
2+
.DS_Store

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
language: bash
3+
4+
install:
5+
- make setup
6+
7+
script:
8+
- make test

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Tom Marshall
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
setup:
2+
@rm -rf vendor
3+
@mkdir -p vendor
4+
git clone --depth 1 git://github.com/sstephenson/bats.git vendor/bats
5+
git clone --depth 1 git://github.com/ztombol/bats-assert.git vendor/bats-assert
6+
git clone --depth 1 git://github.com/ztombol/bats-support.git vendor/bats-support
7+
8+
test:
9+
vendor/bats/bin/bats test
10+
11+
.PHONY: setup test

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# git-good-commit
2+
3+
[![Build Status](https://travis-ci.org/tommarshall/git-good-commit.svg?branch=master)](https://travis-ci.org/tommarshall/git-good-commit)
4+
5+
Git hook to help you write good commit messages.
6+
7+
Validates commit messages conform to six of [the seven rules of a great git commit message](http://chris.beams.io/posts/git-commit/), plus a couple of extras:
8+
9+
1. [Separate subject from body with a blank line](http://chris.beams.io/posts/git-commit/#separate)
10+
2. [Limit the subject line to 50 characters](http://chris.beams.io/posts/git-commit/#limit-50)
11+
3. [Capitalize the subject line](http://chris.beams.io/posts/git-commit/#capitalize)
12+
4. [Do not end the subject line with a period](http://chris.beams.io/posts/git-commit/#end)
13+
5. [Use the imperative mood in the subject line](http://chris.beams.io/posts/git-commit/#imperative)
14+
6. [Wrap the body at 72 characters](http://chris.beams.io/posts/git-commit/#wrap-72)
15+
7. ~~[Use the body to explain what and why vs. how](http://chris.beams.io/posts/git-commit/#why-not-how)~~ _- you're on your own with this one_
16+
8. Do no write single worded commits
17+
9. Do not start the subject line with whitespace
18+
19+
Offers an interactive prompt if any of the rules are detected to be broken.
20+
21+
![git-good-commit animated demo](demo.gif)
22+
23+
## Installation
24+
25+
### Single repository
26+
27+
At the root of the repository, run:
28+
29+
```sh
30+
curl https://cdn.rawgit.com/tommarshall/git-good-commit/v0.1.0/hook.sh > .git/hooks/commit-msg && chmod +x .git/hooks/commit-msg
31+
```
32+
33+
### Globally
34+
35+
To use the hook globally, you can use `git-init`'s template directory:
36+
37+
```sh
38+
mkdir -p ~/.git-template/hooks
39+
git config --global init.templatedir '~/.git-template'
40+
curl https://cdn.rawgit.com/tommarshall/git-good-commit/v0.1.0/hook.sh > ~/.git-template/hooks/commit-msg && chmod +x ~/.git-template/hooks/commit-msg
41+
```
42+
43+
The hook will now be present after any `git init` or `git clone`. You can [safely re-run `git init`](http://stackoverflow.com/a/5149861/885540) on any existing repositories to add the hook there.
44+
45+
---
46+
47+
_If you're security conscious, you may be reasonably suspicious of [curling executable files](https://www.seancassidy.me/dont-pipe-to-your-shell.html). In this case you're on HTTPS throughout, and not piping directly to execution, so you can check contents and the hash against MD5 `68d8a2eb4d54ba04e89cbe8bedbeca53` for v0.1.0._
48+
49+
## Usage
50+
51+
```sh
52+
echo "apple" > ./bar.txt
53+
git add fruits.txt
54+
55+
# should warn you that the subject line is not capitalised, and offer an interactive prompt.
56+
git commit -m 'add fruits.txt'
57+
```
58+
59+
### Options
60+
61+
```
62+
e - edit commit message
63+
y - proceed with commit
64+
n - abort commit
65+
? - print help
66+
```
67+
68+
## Dependencies
69+
70+
None, other than Bash.
71+
72+
## Credits
73+
74+
* http://chris.beams.io/posts/git-commit
75+
* http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
76+
* https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines
77+
* Tim Perry's excellent [git-confim](https://github.com/pimterry/git-confirm) hook, which provided the inspiration and much of the scaffolding for this hook.

demo.gif

685 KB
Loading

hook.sh

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#!/usr/bin/env bash
2+
3+
#
4+
# git-good-commit(1) - Git hook to help you write good commit messages.
5+
# Released under the MIT License.
6+
#
7+
# Version 0.1.0
8+
#
9+
# https://github.com/tommarshall/git-good-commit
10+
#
11+
12+
COMMIT_MSG_FILE="$1"
13+
COMMIT_MSG_LINES=
14+
SKIP_DISPLAY_WARNINGS=0
15+
WARNINGS=
16+
17+
RED=
18+
YELLOW=
19+
BLUE=
20+
WHITE=
21+
NC=
22+
23+
#
24+
# Set colour variables if the output should be coloured.
25+
#
26+
27+
set_colors() {
28+
local default_color=$(git config --get color.ui || echo 'auto')
29+
if [[ $default_color == 'always' ]] || [[ $default_color == 'auto' && -t 1 ]]; then
30+
RED='\033[1;31m'
31+
YELLOW='\033[1;33m'
32+
BLUE='\033[1;34m'
33+
WHITE='\033[1;37m'
34+
NC='\033[0m' # No Color
35+
fi
36+
}
37+
38+
#
39+
# Output prompt help information.
40+
#
41+
42+
prompt_help() {
43+
echo -e "${RED}$(cat <<-EOF
44+
e - edit commit message
45+
y - proceed with commit
46+
n - abort commit
47+
? - print help
48+
EOF
49+
)${NC}"
50+
}
51+
52+
#
53+
# Add a warning with <line_number> and <msg>.
54+
#
55+
56+
add_warning() {
57+
local line_number=$1
58+
local warning=$2
59+
WARNINGS[$line_number]="${WARNINGS[$line_number]}$warning;"
60+
}
61+
62+
#
63+
# Output warnings.
64+
#
65+
66+
display_warnings() {
67+
if [ $SKIP_DISPLAY_WARNINGS -eq 1 ]; then
68+
# if the warnings were skipped then they should be displayed next time
69+
SKIP_DISPLAY_WARNINGS=0
70+
return
71+
fi
72+
73+
for i in "${!WARNINGS[@]}"; do
74+
printf "%-74s ${WHITE}%s${NC}\n" "${COMMIT_MSG_LINES[$(($i-1))]}" "[line ${i}]"
75+
IFS=';' read -ra WARNINGS_ARRAY <<< "${WARNINGS[$i]}"
76+
for ERROR in "${WARNINGS_ARRAY[@]}"; do
77+
echo -e " ${YELLOW}- ${ERROR}${NC}"
78+
done
79+
done
80+
}
81+
82+
#
83+
# Read the contents of the commit msg into an array of lines.
84+
#
85+
86+
read_commit_msg() {
87+
# reset commit_msg_lines
88+
COMMIT_MSG_LINES=()
89+
90+
# read commit message into lines array
91+
while IFS= read -r; do
92+
[[ $REPLY =~ ^# ]]
93+
test $? -eq 0 || COMMIT_MSG_LINES+=("$REPLY")
94+
done < <(cat $COMMIT_MSG_FILE)
95+
}
96+
97+
#
98+
# Validate the contents of the commmit msg agains the good commit guidelines.
99+
#
100+
101+
validate_commit_message() {
102+
# reset warnings
103+
WARNINGS=()
104+
105+
# if the commit is empty there's nothing to validate, we can return here
106+
COMMIT_MSG_STR="${COMMIT_MSG_LINES[*]}"
107+
test -n "${COMMIT_MSG_STR[*]// }" || return;
108+
109+
# 1. Separate subject from body with a blank line
110+
# ------------------------------------------------------------------------------
111+
112+
test ${#COMMIT_MSG_LINES[@]} -lt 1 || test -z "${COMMIT_MSG_LINES[1]}"
113+
test $? -eq 0 || add_warning 2 "Separate subject from body with a blank line"
114+
115+
# 2. Limit the subject line to 50 characters
116+
# ------------------------------------------------------------------------------
117+
118+
test "${#COMMIT_MSG_LINES[0]}" -le 50
119+
test $? -eq 0 || add_warning 1 "Limit the subject line to 50 characters (${#COMMIT_MSG_LINES[0]} chars)"
120+
121+
# 3. Capitalize the subject line
122+
# ------------------------------------------------------------------------------
123+
124+
[[ ${COMMIT_MSG_LINES[0]} =~ ^[[:blank:]]*([[:upper:]]{1}[[:lower:]]*|[[:digit:]]+)([[:blank:]]|[[:punct:]]|$) ]]
125+
test $? -eq 0 || add_warning 1 "Capitalize the subject line"
126+
127+
# 4. Do not end the subject line with a period
128+
# ------------------------------------------------------------------------------
129+
130+
[[ ${COMMIT_MSG_LINES[0]} =~ [^\.]$ ]]
131+
test $? -eq 0 || add_warning 1 "Do not end the subject line with a period"
132+
133+
# 5. Use the imperative mood in the subject line
134+
# ------------------------------------------------------------------------------
135+
136+
IMPERATIVE_MOOD_BLACKLIST=(
137+
added adds adding
138+
avoided avoids avoiding
139+
amended amends amending
140+
bumped bumps bumping
141+
changed changes changing
142+
checked checks checking
143+
committed commits committing
144+
copied copies copying
145+
corrected corrects correcting
146+
created creates creating
147+
deleted deletes deleting
148+
fixed fixes fixing
149+
implemented implements implementing
150+
improved improves improving
151+
introduced introduces introducing
152+
moved moves moving
153+
pruned prunes pruning
154+
refactored refactors refactoring
155+
removed removes removing
156+
renamed renames renaming
157+
replaced replaces replacing
158+
resolved resolves resolving
159+
showed shows showing
160+
tested tests testing
161+
updated updates updating
162+
used uses using
163+
)
164+
165+
# enable case insensitive match
166+
shopt -s nocasematch
167+
168+
for BLACKLISTED_WORD in "${IMPERATIVE_MOOD_BLACKLIST[@]}"; do
169+
[[ ${COMMIT_MSG_LINES[0]} =~ $BLACKLISTED_WORD ]]
170+
test $? -eq 0 && add_warning 1 "Use the imperative mood in the subject line, e.g 'fix' not 'fixes'" && break
171+
done
172+
173+
# disable case insensitive match
174+
shopt -u nocasematch
175+
176+
# 6. Wrap the body at 72 characters
177+
# ------------------------------------------------------------------------------
178+
179+
URL_REGEX='^[[:blank:]]*(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]'
180+
181+
for i in "${!COMMIT_MSG_LINES[@]}"; do
182+
LINE_NUMBER=$((i+1))
183+
test "${#COMMIT_MSG_LINES[$i]}" -le 72 || [[ ${COMMIT_MSG_LINES[$i]} =~ $URL_REGEX ]]
184+
test $? -eq 0 || add_warning $LINE_NUMBER "Wrap the body at 72 characters (${#COMMIT_MSG_LINES[$i]} chars)"
185+
done
186+
187+
# 7. Use the body to explain what and why vs. how
188+
# ------------------------------------------------------------------------------
189+
190+
# ?
191+
192+
# 8. Do no write single worded commits
193+
# ------------------------------------------------------------------------------
194+
195+
COMMIT_SUBJECT_WORDS=(${COMMIT_MSG_LINES[0]})
196+
test "${#COMMIT_SUBJECT_WORDS[@]}" -gt 1
197+
test $? -eq 0 || add_warning 1 "Do no write single worded commits"
198+
199+
# 9. Do not start the subject line with whitespace
200+
# ------------------------------------------------------------------------------
201+
202+
[[ ${COMMIT_MSG_LINES[0]} =~ ^[[:blank:]]+ ]]
203+
test $? -eq 1 || add_warning 1 "Do not start the subject line with whitespace"
204+
}
205+
206+
#
207+
# It's showtime.
208+
#
209+
210+
set_colors
211+
212+
if tty >/dev/null 2>&1; then
213+
TTY=$(tty)
214+
else
215+
TTY=/dev/tty
216+
fi
217+
218+
while true; do
219+
220+
read_commit_msg
221+
222+
validate_commit_message
223+
224+
# if there are no WARNINGS are empty then we're good to break out of here
225+
test ${#WARNINGS[@]} -eq 0 && exit 0;
226+
227+
display_warnings
228+
229+
# Ask the question (not using "read -p" as it uses stderr not stdout)
230+
echo -en "${BLUE}Proceed with commit? [e/y/n/?] ${NC}"
231+
232+
# Read the answer
233+
read REPLY < "$TTY"
234+
235+
# Check if the reply is valid
236+
case "$REPLY" in
237+
E*|e*) $EDITOR "$COMMIT_MSG_FILE" < $TTY; continue ;;
238+
Y*|y*) exit 0 ;;
239+
N*|n*) exit 1 ;;
240+
*) SKIP_DISPLAY_WARNINGS=1; prompt_help; continue ;;
241+
esac
242+
243+
done

0 commit comments

Comments
 (0)