diff --git a/readme.md b/README.md similarity index 90% rename from readme.md rename to README.md index 47a03a2..6d39a02 100644 --- a/readme.md +++ b/README.md @@ -31,13 +31,13 @@ Or: ## authorship This is more tricky, and requires Ruby. First run the -`determine_authors.sh` script to get a count of all the authorship +`commits_stats.sh` script to get a count of all the authorship events in the repo; then run the `tally_authors.rb` Ruby script to sort the authorship events by name. You can do all that as a one-liner in bash like this: - ruby tally_authors.rb <(cd /project && /this_dir/determine_authors.sh "js") + ruby tally_authors.rb <(cd /project && /this_dir/commits_stats.sh -d "js") The code isn't tricky at all, and the output is easy to understand, but it's also easy to misconstrue. That's the tricky part. A developer @@ -47,7 +47,7 @@ commits to small ones, or for several other possible reasons. Exercise good judgement. Also, where `file_stats.sh` will examine multiple file types, -`determine_authors.sh` and `tally_authors.rb` assumes you're only +`commits_stats.sh` and `tally_authors.rb` assumes you're only working with one type of file at a time. Pull requests welcome! Caveat: Running this code against extremely large projects with very diff --git a/commits_stats.sh b/commits_stats.sh new file mode 100755 index 0000000..1eee364 --- /dev/null +++ b/commits_stats.sh @@ -0,0 +1,130 @@ +#!/bin/bash +CMD=`basename $0`; + +# prints the usage message +function usage() { + echo -e "$CMD is a script to extract some basic info on git commits and +authors from the files contained in the current directory. +If invoked without parameters it will output the overall list of commits +count per author (-o) followed by the list of files with the commit count +for each author. +It can also be used to extract information on specific file extensions.\n +Syntax: $CMD [-h|-o|[-c|-a|-d] ] +\t-h: prints this help +\t-o: displays just the overall commits count per author +\t-c: displays the files ordered by number of commits +\t-a: displays overall commits, and list of commits by author per each file +\t-d: just extract the authors, to be used in conjunction with tally_authors.rb +\t: display authors commit count for specific filetypes +\te.g. $CMD js # examines authorship for *.js files +\n" +} + +# returns the list of authors +function extract_authors { + for filename in $(find . -iname "${PATTERN}"); do + git log $filename | grep "Author" + done +} + +# uses the log of a specific file to capture the number of commits per person +function count_author_commits_per_file { + if [[ ! -n $1 ]]; then + filename='' + else + filename=$1; + fi + git log --pretty=format:"%an <%ae>" $filename | awk '{ + authorsCount[$NF]++; + authors[$NF]=$0; + } + END { + for (i in authorsCount) + printf("%d\t%s\n", authorsCount[i], authors[i]); + }'; +} + +function tally_authors { + if [[ -n $OVERALL_ONLY ]]; then + echo "Overall"; + count_author_commits_per_file | sort -k1,1nr -k2,2; + return + fi + for filename in $(find . -iname "${PATTERN}" -not -wholename "*.git*" -not -wholename "*.svn*"); do + authors=`count_author_commits_per_file $filename | sort -k1,1nr -k2,2`; + if [ "$authors" = "" ]; then + continue; + fi + if [ $filename = '.' ]; then + echo "Overall" + else + echo $filename; + fi + echo "$authors"; + done +} + +function count_commits_per_file { + for filename in $(find . -iname "${PATTERN}" -not -wholename "*.git*" -not -wholename "*.svn*"); do + commits_count=`git rev-list HEAD --count ${filename}`; + if [ $commits_count -gt 0 ]; then + echo -e "${commits_count}\t${filename}"; + fi + done +} + +function sort_files_by_commits { + file_commits=`count_commits_per_file | sort -k1,1nr -k2,2`; + if [[ -n $ALL_STATS ]]; then + echo "${file_commits}" | while read line; do + tot_commits=`echo ${line} | awk '{ print $1 }'`; + filename=`echo "${line}" | awk '{ sub(/.*\t/, ""); print }'`; + echo ${filename}; + echo "${tot_commits} total commit(s)" + count_author_commits_per_file $filename | sort -k1,1nr -k2,2; + echo ""; + done + else + echo "$file_commits"; + fi +} + +while getopts ":hocad" Option +do + case $Option in + h) usage + exit 0 + ;; + o) OVERALL_ONLY=true + ;; + c) FILE_COMMITS_ONLY=true + ;; + a) ALL_STATS=true + ;; + d) DEPRECATED=true + ;; + esac +done + +# Decrements the argument pointer so it points to next argument. +# $1 now references the first non-option item supplied on the command-line +# if one exists. +shift $(($OPTIND - 1)) + +if [ $# -eq 1 ]; then + PATTERN="*.$1"; +else + PATTERN='*.*'; +fi + +if [[ -n $FILE_COMMITS_ONLY ]]; then + sort_files_by_commits; +elif [[ -n $ALL_STATS ]]; then + sort_files_by_commits; +elif [[ -n $DEPRECATED ]]; then + extract_authors; +else + tally_authors "${PATTERN}"; +fi + +exit 0 diff --git a/file_stats.sh b/file_stats.sh old mode 100644 new mode 100755 index 929debd..98c09ae --- a/file_stats.sh +++ b/file_stats.sh @@ -1,3 +1,5 @@ +#!/bin/bash + function git_history { git log $filename | grep "Date: " }