├── .ccignore ├── test-files ├── basic.cpp └── thing.js ├── LICENSE ├── post-commit ├── test.sh ├── prepare-commit-msg └── README.md /.ccignore: -------------------------------------------------------------------------------- 1 | README.md 2 | prepare-commit-msg 3 | post-commit 4 | test.sh 5 | test-files/basic.cpp 6 | testfiles/thing.js 7 | LICENSE 8 | -------------------------------------------------------------------------------- /test-files/basic.cpp: -------------------------------------------------------------------------------- 1 | // This is a normal comment 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main () { 8 | 9 | // The following is a "commit-comment" 10 | // @commit: Stored Hello World in string 11 | string phrase = "Hello World!"; 12 | cout << phrase << endl; 13 | 14 | int a = 10; // @commit - Inline commit comment 15 | int b = 33; /* @commit ... Another inline commit comment */ 16 | int c = 90; /*** @commit: final inline commit comment ***/ 17 | 18 | // Invalids comment syntax 19 | 20 | // @commit: Added a new function to basic.cpp 21 | function (string) { 22 | console.log(string) 23 | } 24 | 25 | / @commit: Something 26 | / @ commit A space 27 | @commit no comment 28 | @commitnospaces 29 | @commit@commit: double commit symbols 30 | } 31 | -------------------------------------------------------------------------------- /test-files/thing.js: -------------------------------------------------------------------------------- 1 | // JavaScript taken from https://github.com/thebearjew/thebearjew.github.io 2 | // This code was used open links, directing outside of jry.io, to open in a new browser tab. 3 | 4 | // Adds attribute "target=_blank" to links to all external sites 5 | function handleExternalLinks () { 6 | var host = location.host 7 | var allLinks = document.querySelectorAll('a') 8 | forEach(allLinks, function (elem, index) { 9 | checkExternalLink(elem, host) 10 | }) 11 | } 12 | 13 | function checkExternalLink (item, hostname) { 14 | var href = item.href 15 | var itemHost = href.replace(/https?:\/\/([^\/]+)(.*)/, '$1') 16 | if (itemHost !== '' && itemHost !== hostname) { 17 | // console.log('Changing ' + item + ' to target=_blank') 18 | item.target = '_blank' 19 | } 20 | } 21 | 22 | // NodeList forEach function 23 | // @commit: Added forEach function for NodeList elements 24 | /* @commit added Minos's name to the funciton */ 25 | function forEach (array, callback, scope) { 26 | for (var i = 0; i < array.length; ++i) { 27 | callback.call(scope, array[i], i) 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jacob Young 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 | 23 | -------------------------------------------------------------------------------- /post-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2016 Jacob Young 4 | # MIT License 5 | # 6 | # Remove @commit comments from source files 7 | 8 | IGNORED=( ".ccignore" ) 9 | 10 | # If the script is deployed to the .git directory, 11 | # move to the repository directory so .ccignore file 12 | # can be found 13 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 14 | 15 | if which pcregrep > /dev/null; then 16 | grep_git="pcregrep -o '^.*(?=(\.git))'" 17 | else 18 | grep_git="grep -Po '^.*(?=(\.git))'" 19 | fi 20 | 21 | if echo "$DIR" | grep -qs 'git'; then 22 | cd "$( echo "$DIR" | eval "$grep_git" )" 23 | fi 24 | 25 | if [ -f ".ccignore" ]; then 26 | while IFS='' read -r line || [[ -n "$line" ]]; do 27 | IGNORED+=( "$line" ) 28 | done < ".ccignore" 29 | fi 30 | 31 | IFS=$'\n' ADDED_FILES=( $(git ls-files) ) 32 | 33 | for i in ${!ADDED_FILES[@]}; do 34 | file_path=${ADDED_FILES["$i"]} 35 | filename=$( basename "$file_path" ) 36 | if [[ "${IGNORED[*]}" =~ "${filename}" ]]; then 37 | # File should not be read - remove 38 | unset ADDED_FILES["$i"] 39 | fi 40 | done 41 | 42 | for fn in "${ADDED_FILES[@]}"; do 43 | # Uncomment sed command to remove '// @commit' comments in all files 44 | sed -ri 's/(([#;*]|\/{1,2})?(\s+)?@commit([-:.\s]+)?)([^\r\n*\/]+)//' "$fn" # GNU sed compatible only 45 | # Uncomment sed command to replace multiple empty lines with a single empty line 46 | # sed '/^$/N;/^\n$/D' "$fn" # GNU sed compatible only 47 | done 48 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2016 Jacob Young 4 | # MIT License 5 | # 6 | # This script will append a list of added/removed features to a commit message. 7 | # It reads comments following a '@commit' symbol and constructs an unordered list of them. 8 | # After comments are read, they are removed from the file they are found 9 | # 10 | # Example: 11 | # //@commit: Added new link parsing function 12 | # 13 | # Output (in commit message): 14 | # - Added new link parsing funciton 15 | 16 | # Arg 1: SHOW_LINES (T/F) 17 | SHOW_LINES=$1 18 | 19 | IGNORED=( ".ccignore" ) 20 | 21 | # If the script is deployed to the .git directory, 22 | # move to the repository directory so .ccignore file 23 | # can be found 24 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 25 | if echo "$DIR" | grep -qs 'git'; then 26 | cd "$( echo "$DIR" | grep -Po '^.*(?=(\.git))' )" 27 | fi 28 | 29 | if which pcregrep > /dev/null; then 30 | grep_search="pcregrep -o2 '((?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?)([^\r\n*\/]+)'" 31 | N_grep_search="pcregrep -o2 -n '((?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?)([^\r\n*\/]+)'" 32 | else 33 | grep_search="grep -Po '(?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?\K([^\r\n*\/]+)'" 34 | N_grep_search="grep -Po -n '(?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?\K([^\r\n*\/]+)'" 35 | fi 36 | 37 | IFS=$'\n' ADDED_FILES=( $(git ls-files) ) 38 | for i in ${!ADDED_FILES[@]}; do 39 | 40 | file_path=${ADDED_FILES["$i"]} 41 | filename=$( basename "$file_path" ) 42 | extension="${filename##*.}" 43 | # File should be removed if filename or extension type has been specified in .ccignore 44 | if [[ "${IGNORED[*]}" =~ "${filename}" || "${IGNORED[*]}" =~ "${extension}" ]]; then 45 | unset ADDED_FILES["$i"] 46 | fi 47 | done 48 | 49 | comments="" 50 | for fn in "${ADDED_FILES[@]}"; do 51 | if [ "$SHOW_LINES" = true ]; then 52 | temp=$(cat "$fn" | eval "$N_grep_search" | tr -s '[:space:]' | sed 's/[[:blank:]]*$//' | awk -v fname=$fn -F':' '{ $1 ="[" fname "#" $1 "]"; print}') 53 | else 54 | temp=$( cat "$fn" | eval "$grep_search" | tr -s '[:space:]' | sed 's/[[:blank:]]*$//' ) 55 | fi 56 | if [[ "$temp" != "" ]]; then 57 | comments+="$temp" 58 | comments+="\n" 59 | fi 60 | done 61 | comments=$(echo -en "$comments" | sed -e 's/^/- /') 62 | echo -n "$comments" 63 | -------------------------------------------------------------------------------- /prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2016 Jacob Young 4 | # MIT License 5 | # 6 | # This script will append a list of added/removed features to a commit message. 7 | # It reads comments following a '@commit' symbol and constructs an unordered list of them. 8 | # After comments are read, they are removed from the file they are found 9 | # 10 | # Example: 11 | # //@commit: Added new link parsing function 12 | # 13 | # Output (in commit message): 14 | # - Added new link parsing funciton 15 | 16 | # Arg 1: SHOW_LINES (T/F) 17 | SHOW_LINES=true 18 | 19 | IGNORED=( ".ccignore" ) 20 | 21 | # If the script is deployed to the .git directory, 22 | # move to the repository directory so .ccignore file 23 | # can be found 24 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 25 | if echo "$DIR" | grep -qs 'git'; then 26 | cd "$( echo "$DIR" | grep -Po '^.*(?=(\.git))' )" 27 | fi 28 | 29 | if [ -f ".ccignore" ]; then 30 | while IFS='' read -r line || [[ -n "$line" ]]; do 31 | IGNORED+=( "$line" ) 32 | done < ".ccignore" 33 | fi 34 | 35 | if which pcregrep > /dev/null; then 36 | grep_search="pcregrep -o2 '((?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?)([^\r\n*\/]+)'" 37 | N_grep_search="pcregrep -o2 -n '((?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?)([^\r\n*\/]+)'" 38 | else 39 | grep_search="grep -Po '(?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?\K([^\r\n*\/]+)'" 40 | N_grep_search="grep -Po -n '(?:[#;*]|(?:\/{2}))(?:\s+)?@commit(?:[-:.\s]+)?\K([^\r\n*\/]+)'" 41 | fi 42 | 43 | IFS=$'\n' ADDED_FILES=( $(git ls-files) ) 44 | for i in ${!ADDED_FILES[@]}; do 45 | file_path=${ADDED_FILES["$i"]} 46 | filename=$( basename "$file_path" ) 47 | extension="${filename##*.}" 48 | if [[ "${IGNORED[*]}" =~ "${filename}" || "${IGNORED[*]}" =~ "${extension}" ]]; then 49 | # File should not be read - remove 50 | unset ADDED_FILES["$i"] 51 | fi 52 | done 53 | 54 | comments="" 55 | for fn in "${ADDED_FILES[@]}"; do 56 | if [ "$SHOW_LINES" = true ]; then 57 | temp=$(cat "$fn" | eval "$N_grep_search" | tr -s '[:space:]' | sed 's/[[:blank:]]*$//' | awk -v fname=$fn -F':' '{ $1 ="[" fname "#" $1 "]"; print}') 58 | else 59 | temp=$( cat "$fn" | eval "$grep_search" | tr -s '[:space:]' | sed 's/[[:blank:]]*$//' ) 60 | fi 61 | if [[ "$temp" != "" ]]; then 62 | comments+="$temp" 63 | comments+="\n" 64 | fi 65 | done 66 | 67 | comments=$(echo -en "$comments" | sed -e 's/^/- /') 68 | 69 | grep -qs "^$comments" "$1" 70 | if [ $? -eq 1 ]; then 71 | if [[ "$comments" == "" ]]; then 72 | exit 0 73 | else 74 | # Prepend to commit file 75 | echo -e "\n\n$comments" | cat - "$1" > temp_cmmt_msg && mv temp_cmmt_msg "$1" 76 | fi 77 | fi 78 | 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Commit Comments 2 | 3 | Commit Comments automatically create a bulleted list of changes from comments in your code. Write comments using `@commit` keyword, and they will be added to your commit message when it's time to commit. 4 | 5 | It works by using two [Git hooks](https://www.kernel.org/pub/software/scm/git/docs/githooks.html) (**prepare-commit-m sg** and **post-commit**) to 6 | search your repository for @commit comments and construct a clean list of 7 | changes. 8 | 9 | Once you've successfully committed, @commit comments are removed from 10 | your files 11 | 12 | ### Installation 13 | 14 | Clone the repository and move the `prepare-commit-msg` and `post-commit` files. 15 | 16 | ``` 17 | $ git clone https://github.com/thebearjew/commit-comments.git 18 | $ cd commit-comments 19 | $ chmod a+x prepare-commit-msg post-commit 20 | $ cp prepare-commit-msg post-commit your-repository/.git/hooks 21 | ``` 22 | 23 | ### Usage 24 | 25 | As you're writing code, drop `// @commit` comments anywhere a significant change has been made. 26 | 27 | Commit comments work with (inline & standalone): 28 | 29 | - C-like comments (C/C++, Java, JavaScript, etc.) `//`, `/* */` 30 | - Python/Ruby/Perl `#` 31 | - Assembly `;` 32 | 33 | Example: 34 | 35 | ```js 36 | foo.js 37 | 38 | // @commit: Added a parameter to helloWorld function 39 | function helloWorld(phrase) { 40 | console.log('Hello World + ' + phrase); /* @commit - Concatenated strings */ 41 | } 42 | ``` 43 | 44 | Output in Git commit message 45 | 46 | ``` 47 | # Commit title goes here 48 | 49 | - [foo.js#1] Added a parameter to helloWorld function 50 | - [foo.js#3] Concatenated strings 51 | # Changes to be committed: 52 | # modified: foo.js 53 | # 54 | # Changes not staged for commit: 55 | # ... 56 | ``` 57 | 58 | Comments are removed from the original files. 59 | 60 | ```js 61 | foo.js - after commit 62 | 63 | 64 | function helloWorld(phrase) { 65 | console.log('Hello World + ' + phrase); 66 | } 67 | ``` 68 | 69 | ### Ignoring Files 70 | To ignore some files from being searched, create a `.ccignore` file in your repository and add file names/types. 71 | 72 | ``` 73 | README.md 74 | build.sh 75 | .cpp 76 | ``` 77 | 78 | ### Dependencies 79 | 1. **GNU sed** is required to remove @commit comments in post-commit. 80 | 81 | On Mac OS X, the default sed is from the FreeBSD distribution. To download the GNU sed version, use [Brew](http://brew.sh) 82 | 83 | ``` 84 | $ brew install gnu-sed --with-default-names 85 | ``` 86 | 87 | Without the `--with-default-names` option, the command will be downloaded as `gsed`. 88 | 89 | 2. **pcregrep** is the primary search utility due to its widespread 90 | portability. 91 | 92 | If pcregrep is not available, GNU grep is used (for Perl RegEx & variable 93 | lookback). 94 | 95 | 96 | ### Contributing & Todo 97 | Contributions to improve simplicity/resolve compatibility would be preferred. If there are useful improvements, tricks, or hacks, please submit a Pull Request and a directory of add-ons and snippets will be created. 98 | 99 | **TODO** 100 | 101 | - [x] Add filename and line number to bulleted commit commets - [suggestion by 102 | joncalhoun](https://news.ycombinator.com/item?id=10904142) on HN 103 | - [ ] Use `git diff --cached --name-status --diff-filter=ACM` in place of `git 104 | ls-files` 105 | - [ ] Develop more test cases (finding edge cases with grep expression) 106 | - [ ] Rewrite sed commands to be POSIX (BSD) compatible regular expressions 107 | - Create more robust regular expression for validating comment syntax 108 | - [ ] Check for multiline block comments 109 | - [ ] Check for closing comment symbols (positive look aheads) 110 | - Programming Languages 111 | - [ ] HTML 112 | - [ ] Fortran 113 | - [ ] AppleScript 114 | 115 | -- 116 | 117 | Special Thanks to Bryan Wyatt for feedback and bug fixes - [@brwyatt](https://twitter.com/brwyatt) 118 | --------------------------------------------------------------------------------