├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── demo.gif ├── hook.sh └── test ├── prompt.bats ├── test_helper.bash └── validation.bats /.editorconfig: -------------------------------------------------------------------------------- 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 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: bash 3 | 4 | install: 5 | - make setup 6 | 7 | script: 8 | - make test 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | 11 | * Add multiple verbs to the imperative mood blacklist. 12 | 13 | ### Fixed 14 | 15 | * Avoid hanging when run non-interactively (@ssbarnea). 16 | * Avoid validating lines after the verbose cut line marker (@walle). 17 | 18 | ## [v0.6.1] - 2017-01-04 19 | 20 | ### Fixed 21 | 22 | * A bug with the edit action introduced in [v0.6.0]. 23 | 24 | ## [v0.6.0] - 2017-01-03 25 | 26 | ### Changed 27 | 28 | * Mirror Git's approach for editor config, instead of just using `$EDITOR`. 29 | 30 | ## [v0.5.0] - 2016-11-22 31 | 32 | ### Changed 33 | 34 | * Only check the first word of the subject for imperative mood. 35 | 36 | ## [v0.4.0] - 2016-11-03 37 | 38 | ### Changed 39 | 40 | * Ignore `fixup!` and `squash!` autosquash flags. 41 | 42 | ## [v0.3.0] - 2016-10-28 43 | 44 | ### Added 45 | 46 | * Add `hooks.goodcommit.color` config. 47 | 48 | ## [v0.2.0] - 2016-09-29 49 | 50 | ### Fixed 51 | 52 | * Ignore trailing whitespace in commit messages. 53 | 54 | ## v0.1.0 - 2016-08-31 55 | 56 | ### Added 57 | 58 | * Initial version. 59 | 60 | [Unreleased]: https://github.com/tommarshall/git-good-commit/compare/v0.6.1...HEAD 61 | [v0.6.1]: https://github.com/tommarshall/git-good-commit/compare/v0.6.0...v0.6.1 62 | [v0.6.0]: https://github.com/tommarshall/git-good-commit/compare/v0.5.0...v0.6.0 63 | [v0.5.0]: https://github.com/tommarshall/git-good-commit/compare/v0.4.0...v0.5.0 64 | [v0.4.0]: https://github.com/tommarshall/git-good-commit/compare/v0.3.0...v0.4.0 65 | [v0.3.0]: https://github.com/tommarshall/git-good-commit/compare/v0.2.0...v0.3.0 66 | [v0.2.0]: https://github.com/tommarshall/git-good-commit/compare/v0.1.0...v0.2.0 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 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 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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 -L https://cdn.rawgit.com/tommarshall/git-good-commit/v0.6.1/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 -L https://cdn.rawgit.com/tommarshall/git-good-commit/v0.6.1/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 `12fd3b8829eead2eff9a72598cbc9f4b` for v0.6.1._ 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 | ## Configuration 69 | 70 | The default colour setting is `auto`, but the hook will use `git`'s `color.ui` config setting if defined. You override the colour setting for the hook with: 71 | 72 | ``` 73 | git config --global hooks.goodcommit.color "never" 74 | ``` 75 | 76 | Supported values are `always`, `auto`, `never` and `false`. 77 | 78 | ## Dependencies 79 | 80 | None, other than Bash. 81 | 82 | ## Credits 83 | 84 | * http://chris.beams.io/posts/git-commit 85 | * http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 86 | * https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines 87 | * Tim Perry's excellent [git-confirm](https://github.com/pimterry/git-confirm) hook, which provided the inspiration and much of the scaffolding for this hook. 88 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tommarshall/git-good-commit/178bfd879e28d213fdf48b6ed8a34b107d7d0af0/demo.gif -------------------------------------------------------------------------------- /hook.sh: -------------------------------------------------------------------------------- 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.6.1 8 | # 9 | # https://github.com/tommarshall/git-good-commit 10 | # 11 | 12 | COMMIT_MSG_FILE="$1" 13 | COMMIT_MSG_LINES= 14 | HOOK_EDITOR= 15 | SKIP_DISPLAY_WARNINGS=0 16 | WARNINGS= 17 | 18 | RED= 19 | YELLOW= 20 | BLUE= 21 | WHITE= 22 | NC= 23 | 24 | # 25 | # Set colour variables if the output should be coloured. 26 | # 27 | 28 | set_colors() { 29 | local default_color=$(git config --get hooks.goodcommit.color || git config --get color.ui || echo 'auto') 30 | if [[ $default_color == 'always' ]] || [[ $default_color == 'auto' && -t 1 ]]; then 31 | RED='\033[1;31m' 32 | YELLOW='\033[1;33m' 33 | BLUE='\033[1;34m' 34 | WHITE='\033[1;37m' 35 | NC='\033[0m' # No Color 36 | fi 37 | } 38 | 39 | # 40 | # Set the hook editor, using the same approach as git. 41 | # 42 | 43 | set_editor() { 44 | # $GIT_EDITOR appears to always be set to `:` when the hook is executed by Git? 45 | # ref: http://stackoverflow.com/q/41468839/885540 46 | # ref: https://github.com/tommarshall/git-good-commit/issues/11 47 | # HOOK_EDITOR=$GIT_EDITOR 48 | test -z "${HOOK_EDITOR}" && HOOK_EDITOR=$(git config --get core.editor) 49 | test -z "${HOOK_EDITOR}" && HOOK_EDITOR=$VISUAL 50 | test -z "${HOOK_EDITOR}" && HOOK_EDITOR=$EDITOR 51 | test -z "${HOOK_EDITOR}" && HOOK_EDITOR='vi' 52 | } 53 | 54 | # 55 | # Output prompt help information. 56 | # 57 | 58 | prompt_help() { 59 | echo -e "${RED}$(cat <<-EOF 60 | e - edit commit message 61 | y - proceed with commit 62 | n - abort commit 63 | ? - print help 64 | EOF 65 | )${NC}" 66 | } 67 | 68 | # 69 | # Add a warning with and . 70 | # 71 | 72 | add_warning() { 73 | local line_number=$1 74 | local warning=$2 75 | WARNINGS[$line_number]="${WARNINGS[$line_number]}$warning;" 76 | } 77 | 78 | # 79 | # Output warnings. 80 | # 81 | 82 | display_warnings() { 83 | if [ $SKIP_DISPLAY_WARNINGS -eq 1 ]; then 84 | # if the warnings were skipped then they should be displayed next time 85 | SKIP_DISPLAY_WARNINGS=0 86 | return 87 | fi 88 | 89 | for i in "${!WARNINGS[@]}"; do 90 | printf "%-74s ${WHITE}%s${NC}\n" "${COMMIT_MSG_LINES[$(($i-1))]}" "[line ${i}]" 91 | IFS=';' read -ra WARNINGS_ARRAY <<< "${WARNINGS[$i]}" 92 | for ERROR in "${WARNINGS_ARRAY[@]}"; do 93 | echo -e " ${YELLOW}- ${ERROR}${NC}" 94 | done 95 | done 96 | } 97 | 98 | # 99 | # Read the contents of the commit msg into an array of lines. 100 | # 101 | 102 | read_commit_message() { 103 | # reset commit_msg_lines 104 | COMMIT_MSG_LINES=() 105 | 106 | # read commit message into lines array 107 | while IFS= read -r; do 108 | 109 | # trim trailing spaces from commit lines 110 | shopt -s extglob 111 | REPLY="${REPLY%%*( )}" 112 | shopt -u extglob 113 | 114 | # ignore all lines after cut line 115 | [[ $REPLY == "# ------------------------ >8 ------------------------" ]] 116 | test $? -eq 1 || break 117 | 118 | # ignore comments 119 | [[ $REPLY =~ ^# ]] 120 | test $? -eq 0 || COMMIT_MSG_LINES+=("$REPLY") 121 | 122 | done < <(cat $COMMIT_MSG_FILE) 123 | } 124 | 125 | # 126 | # Validate the contents of the commit msg against the good commit guidelines. 127 | # 128 | 129 | validate_commit_message() { 130 | # reset warnings 131 | WARNINGS=() 132 | 133 | # capture the subject, and remove the 'squash! ' prefix if present 134 | COMMIT_SUBJECT=${COMMIT_MSG_LINES[0]/#squash! /} 135 | 136 | # if the commit is empty there's nothing to validate, we can return here 137 | COMMIT_MSG_STR="${COMMIT_MSG_LINES[*]}" 138 | test -z "${COMMIT_MSG_STR[*]// }" && return; 139 | 140 | # if the commit subject starts with 'fixup! ' there's nothing to validate, we can return here 141 | [[ $COMMIT_SUBJECT == 'fixup! '* ]] && return; 142 | 143 | # 1. Separate subject from body with a blank line 144 | # ------------------------------------------------------------------------------ 145 | 146 | test ${#COMMIT_MSG_LINES[@]} -lt 1 || test -z "${COMMIT_MSG_LINES[1]}" 147 | test $? -eq 0 || add_warning 2 "Separate subject from body with a blank line" 148 | 149 | # 2. Limit the subject line to 50 characters 150 | # ------------------------------------------------------------------------------ 151 | 152 | test "${#COMMIT_SUBJECT}" -le 50 153 | test $? -eq 0 || add_warning 1 "Limit the subject line to 50 characters (${#COMMIT_SUBJECT} chars)" 154 | 155 | # 3. Capitalize the subject line 156 | # ------------------------------------------------------------------------------ 157 | 158 | [[ ${COMMIT_SUBJECT} =~ ^[[:blank:]]*([[:upper:]]{1}[[:lower:]]*|[[:digit:]]+)([[:blank:]]|[[:punct:]]|$) ]] 159 | test $? -eq 0 || add_warning 1 "Capitalize the subject line" 160 | 161 | # 4. Do not end the subject line with a period 162 | # ------------------------------------------------------------------------------ 163 | 164 | [[ ${COMMIT_SUBJECT} =~ [^\.]$ ]] 165 | test $? -eq 0 || add_warning 1 "Do not end the subject line with a period" 166 | 167 | # 5. Use the imperative mood in the subject line 168 | # ------------------------------------------------------------------------------ 169 | 170 | IMPERATIVE_MOOD_BLACKLIST=( 171 | added adds adding 172 | affixed affixes affixing 173 | adjusted adjusts adjusting 174 | amended amends amending 175 | avoided avoids avoiding 176 | bumped bumps bumping 177 | changed changes changing 178 | checked checks checking 179 | committed commits committing 180 | copied copies copying 181 | corrected corrects correcting 182 | created creates creating 183 | decreased decreases decreasing 184 | deleted deletes deleting 185 | disabled disables disabling 186 | dropped drops dropping 187 | duplicated duplicates duplicating 188 | enabled enables enabling 189 | enhanced enhances enhancing 190 | excluded excludes excluding 191 | extracted extracts extracting 192 | fixed fixes fixing 193 | handled handles handling 194 | implemented implements implementing 195 | improved improves improving 196 | included includes including 197 | increased increases increasing 198 | installed installs installing 199 | introduced introduces introducing 200 | leased leases leasing 201 | managed manages managing 202 | merged merges merging 203 | moved moves moving 204 | normalised normalises normalising 205 | normalized normalizes normalizing 206 | passed passes passing 207 | pointed points pointing 208 | pruned prunes pruning 209 | ran runs running 210 | refactored refactors refactoring 211 | released releases releasing 212 | removed removes removing 213 | renamed renames renaming 214 | replaced replaces replacing 215 | resolved resolves resolving 216 | reverted reverts reverting 217 | sets setting 218 | showed shows showing 219 | swapped swaps swapping 220 | tested tests testing 221 | tidied tidies tidying 222 | updated updates updating 223 | upped ups upping 224 | used uses using 225 | ) 226 | 227 | # enable case insensitive match 228 | shopt -s nocasematch 229 | 230 | for BLACKLISTED_WORD in "${IMPERATIVE_MOOD_BLACKLIST[@]}"; do 231 | [[ ${COMMIT_SUBJECT} =~ ^[[:blank:]]*$BLACKLISTED_WORD ]] 232 | test $? -eq 0 && add_warning 1 "Use the imperative mood in the subject line, e.g 'fix' not 'fixes'" && break 233 | done 234 | 235 | # disable case insensitive match 236 | shopt -u nocasematch 237 | 238 | # 6. Wrap the body at 72 characters 239 | # ------------------------------------------------------------------------------ 240 | 241 | URL_REGEX='^[[:blank:]]*(https?|ftp|file|wss?|git|ssh|data|irc|dat)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' 242 | 243 | for i in "${!COMMIT_MSG_LINES[@]}"; do 244 | LINE_NUMBER=$((i+1)) 245 | test "${#COMMIT_MSG_LINES[$i]}" -le 72 || [[ ${COMMIT_MSG_LINES[$i]} =~ $URL_REGEX ]] 246 | test $? -eq 0 || add_warning $LINE_NUMBER "Wrap the body at 72 characters (${#COMMIT_MSG_LINES[$i]} chars)" 247 | done 248 | 249 | # 7. Use the body to explain what and why vs. how 250 | # ------------------------------------------------------------------------------ 251 | 252 | # ? 253 | 254 | # 8. Do no write single worded commits 255 | # ------------------------------------------------------------------------------ 256 | 257 | COMMIT_SUBJECT_WORDS=(${COMMIT_SUBJECT}) 258 | test "${#COMMIT_SUBJECT_WORDS[@]}" -gt 1 259 | test $? -eq 0 || add_warning 1 "Do no write single worded commits" 260 | 261 | # 9. Do not start the subject line with whitespace 262 | # ------------------------------------------------------------------------------ 263 | 264 | [[ ${COMMIT_SUBJECT} =~ ^[[:blank:]]+ ]] 265 | test $? -eq 1 || add_warning 1 "Do not start the subject line with whitespace" 266 | } 267 | 268 | # 269 | # It's showtime. 270 | # 271 | 272 | set_colors 273 | 274 | set_editor 275 | 276 | if tty >/dev/null 2>&1; then 277 | TTY=$(tty) 278 | else 279 | TTY=/dev/tty 280 | fi 281 | 282 | while true; do 283 | 284 | read_commit_message 285 | 286 | validate_commit_message 287 | 288 | # if there are no WARNINGS are empty then we're good to break out of here 289 | test ${#WARNINGS[@]} -eq 0 && exit 0; 290 | 291 | display_warnings 292 | 293 | # if non-interactive don't prompt and exit with an error 294 | if [ ! -t 1 ] && [ -z ${FAKE_TTY+x} ]; then 295 | exit 1 296 | fi 297 | 298 | # Ask the question (not using "read -p" as it uses stderr not stdout) 299 | echo -en "${BLUE}Proceed with commit? [e/y/n/?] ${NC}" 300 | 301 | # Read the answer 302 | read REPLY < "$TTY" 303 | 304 | # Check if the reply is valid 305 | case "$REPLY" in 306 | E*|e*) $HOOK_EDITOR "$COMMIT_MSG_FILE" < $TTY; continue ;; 307 | Y*|y*) exit 0 ;; 308 | N*|n*) exit 1 ;; 309 | *) SKIP_DISPLAY_WARNINGS=1; prompt_help; continue ;; 310 | esac 311 | 312 | done 313 | -------------------------------------------------------------------------------- /test/prompt.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../vendor/bats-support/load' 4 | load '../vendor/bats-assert/load' 5 | load 'test_helper' 6 | 7 | @test "prompt: is shown when commit has validation warnings" { 8 | echo "n" > $FAKE_TTY 9 | run git commit -m " Add foo bar string to my_file" 10 | 11 | assert_failure 12 | assert_line --partial "Proceed with commit? [e/y/n/?]" 13 | } 14 | 15 | @test "prompt: e opens the editor" { 16 | skip # skip this until I can find a way to solve the interaction loop 17 | echo "e" > $FAKE_TTY 18 | run git commit -m "Oops" 19 | } 20 | 21 | @test "prompt: y proceeds with commit" { 22 | echo "y" > $FAKE_TTY 23 | run git commit -m "Oops" 24 | 25 | assert_success 26 | assert_line --partial "1 file changed, 1 insertion(+)" 27 | } 28 | 29 | @test "prompt: n aborts commit" { 30 | echo "n" > $FAKE_TTY 31 | run git commit -m "Oops" 32 | 33 | assert_failure 34 | refute_line --partial "1 file changed, 1 insertion(+)" 35 | } 36 | 37 | @test "prompt: ? shows help" { 38 | skip # skip this until I can find a way to solve the interaction loop 39 | echo "?" > $FAKE_TTY 40 | run git commit -m "Oops" 41 | 42 | assert_line "e - edit commit message" 43 | } 44 | 45 | @test "prompt: default shows help" { 46 | skip # skip this until I can find a way to solve the interaction loop 47 | echo "FOO" > $FAKE_TTY 48 | run git commit -m "Oops" 49 | 50 | assert_line "e - edit commit message" 51 | } 52 | -------------------------------------------------------------------------------- /test/test_helper.bash: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env bash 2 | 3 | # Set up stubs for faking TTY input 4 | export FAKE_TTY="$BATS_TMPDIR/fake_tty" 5 | function tty() { echo $FAKE_TTY; } 6 | export -f tty 7 | 8 | # Remember where the hook is 9 | BASE_DIR=$(dirname $BATS_TEST_DIRNAME) 10 | # Set up a directory for our git repo 11 | TMP_DIRECTORY=$(mktemp -d) 12 | 13 | setup() { 14 | # Clear initial TTY input 15 | > $FAKE_TTY 16 | 17 | # Set up a git repo 18 | cd $TMP_DIRECTORY 19 | mkdir templates 20 | git -c "init.templatedir=$TMP_DIRECTORY/templates" init 21 | git config user.email "test@git-good-commit" 22 | git config user.name "Git Good Commit Tests" 23 | echo "Foo bar" > my_file 24 | git add my_file 25 | mkdir -p .git/hooks 26 | cp "$BASE_DIR/hook.sh" .git/hooks/commit-msg 27 | } 28 | 29 | teardown() { 30 | if [ $BATS_TEST_COMPLETED ]; then 31 | echo "Deleting $TMP_DIRECTORY" 32 | rm -rf $TMP_DIRECTORY 33 | else 34 | echo "** Did not delete $TMP_DIRECTORY, as test failed **" 35 | fi 36 | 37 | cd $BATS_TEST_DIRNAME 38 | } 39 | -------------------------------------------------------------------------------- /test/validation.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load ../vendor/bats-support/load 4 | load ../vendor/bats-assert/load 5 | load test_helper 6 | 7 | @test "validation: ignores empty commits" { 8 | echo "n" > $FAKE_TTY 9 | run git commit -m '' 10 | 11 | assert_failure 12 | assert_line --partial 'Aborting commit' 13 | } 14 | 15 | @test "validation: ignores comments" { 16 | echo "n" > $FAKE_TTY 17 | run git commit -m "$(cat < $FAKE_TTY 29 | run git commit -m "$(cat < $FAKE_TTY 40 | run git commit -m "$(cat <8 ------------------------ 44 | A line in the body that runs to longer than 72 characters after the verbose cut line 45 | EOF 46 | )" 47 | 48 | assert_success 49 | } 50 | 51 | 52 | # 0. Good commits - control 53 | # ------------------------------------------------------------------------------ 54 | 55 | @test "validation: a 'good' single line message does not show warnings" { 56 | echo "n" > $FAKE_TTY 57 | run git commit -m "Add foo bar string to my_file" 58 | 59 | assert_success 60 | } 61 | 62 | @test "validation: a 'good' multi line message does not show warnings" { 63 | echo "n" > $FAKE_TTY 64 | run git commit -m "$(cat < $FAKE_TTY 103 | run git commit -m "$(cat < $FAKE_TTY 118 | run git commit -m "Add foo bar string to my_file - As requested by Jon" 119 | 120 | assert_failure 121 | assert_line --partial "Limit the subject line to 50 characters (51 chars)" 122 | } 123 | 124 | @test "validation: ignores squash! prefix when checking subject line length" { 125 | echo "n" > $FAKE_TTY 126 | run git commit -m "squash! Add foo bar string to my_file - As requested by Jo" 127 | 128 | assert_success 129 | refute_line --partial "Limit the subject line to 50 characters" 130 | } 131 | 132 | # 3. Capitalize the subject line 133 | # ------------------------------------------------------------------------------ 134 | 135 | @test "validation: subject line starting with lowercase word shows warning" { 136 | echo "n" > $FAKE_TTY 137 | run git commit -m "add foo bar string to my_file" 138 | 139 | assert_failure 140 | assert_line --partial "Capitalize the subject line" 141 | } 142 | 143 | @test "validation: subject line starting with with uppercase word shows warning" { 144 | echo "n" > $FAKE_TTY 145 | run git commit -m "ADD foo bar string to my_file" 146 | 147 | assert_failure 148 | assert_line --partial "Capitalize the subject line" 149 | } 150 | 151 | @test "validation: subject line starting with a symbol shows warning" { 152 | echo "n" > $FAKE_TTY 153 | run git commit -m "- Add foo bar string to my_file" 154 | 155 | assert_failure 156 | assert_line --partial "Capitalize the subject line" 157 | } 158 | 159 | @test "validation: subject line starting with a number does not show a warning" { 160 | echo "n" > $FAKE_TTY 161 | run git commit -m "5014 - Add foo bar string to my_file" 162 | 163 | assert_success 164 | refute_line --partial "Capitalize the subject line" 165 | } 166 | 167 | @test "validation: ignores squash! prefix when checking subject line capitalisation" { 168 | echo "n" > $FAKE_TTY 169 | run git commit -m "squash! Add foo bar string to my_file" 170 | 171 | assert_success 172 | refute_line --partial "Capitalize the subject line" 173 | } 174 | 175 | # 4. Do not end the subject line with a period 176 | # ------------------------------------------------------------------------------ 177 | 178 | @test "validation: subject line ending with a period shows warning" { 179 | echo "n" > $FAKE_TTY 180 | run git commit -m "Add foo bar string to my_file." 181 | 182 | assert_failure 183 | assert_line --partial "Do not end the subject line with a period" 184 | } 185 | 186 | # 5. Use the imperative mood in the subject line 187 | # ------------------------------------------------------------------------------ 188 | 189 | @test "validation: subject line starting with 'fixes' shows warning" { 190 | echo "n" > $FAKE_TTY 191 | run git commit -m "Fixes for broken stuff" 192 | 193 | assert_failure 194 | assert_line --partial "Use the imperative mood in the subject line" 195 | } 196 | 197 | @test "validation: subject line starting with 'fixed' shows warning" { 198 | echo "n" > $FAKE_TTY 199 | run git commit -m "Fixed bug with Y" 200 | 201 | assert_failure 202 | assert_line --partial "Use the imperative mood in the subject line" 203 | } 204 | 205 | @test "validation: subject line starting with 'fixing' shows warning" { 206 | echo "n" > $FAKE_TTY 207 | run git commit -m "Fixing behavior of X" 208 | 209 | assert_failure 210 | assert_line --partial "Use the imperative mood in the subject line" 211 | } 212 | 213 | @test "validation: subject line in imperative mood with 'fixes' does not show warning" { 214 | echo "n" > $FAKE_TTY 215 | run git commit -m "Remove the temporary fixes to Y" 216 | 217 | assert_success 218 | refute_line --partial "Use the imperative mood in the subject line" 219 | } 220 | 221 | @test "validation: body with 'fixes', 'fixed', 'fixing' does not show warning" { 222 | run git commit -m "$(cat < $FAKE_TTY 240 | run git commit -m "$(cat < $FAKE_TTY 282 | run git commit -m "Quickfix" 283 | 284 | assert_failure 285 | assert_line --partial "Do no write single worded commits" 286 | } 287 | 288 | # 9. Do not start the subject line with whitespace 289 | # ------------------------------------------------------------------------------ 290 | 291 | @test "validation: subject line starting with whitespace shows warning" { 292 | echo "n" > $FAKE_TTY 293 | run git commit -m " Add foo bar string to my_file" 294 | 295 | assert_failure 296 | assert_line --partial "Do not start the subject line with whitespace" 297 | } 298 | 299 | --------------------------------------------------------------------------------