Last active
October 7, 2022 19:47
-
-
Save amiralles/c2c104294cd0ef1ceac47b3c5ba2ef4f to your computer and use it in GitHub Desktop.
Shell script to mine your git repos.Copy this into your bin directory and run gitmine without arguments to see a list of available commands.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
function frequently_changed_files { | |
git log --name-only --pretty=format: | sort | uniq -c | sort -nr | |
} | |
# Find classes names on a given file. | |
function find_classes { | |
filename=$1 | |
# Possible matches: | |
# class foo | |
# public class foo | |
# public abstract class foo | |
# TODO: Add partials. | |
awk '$1=="class" { print $2}; | |
$2=="class" { print $3}; | |
$3=="class" { print $4}; | |
' <(grep "class " $filename) | |
} | |
# Count references to classes on a given file. | |
function ref_count { | |
filename=$1 | |
tmpfile=$2 | |
while read -r name | |
do | |
grep "$name" "$filename" | \ | |
wc -l | \ | |
xargs echo $name | \ | |
awk '{print $2, $1}' | |
done < "$tmpfile" | |
} | |
# Find classes and count references. | |
function find_and_count { | |
# TODO: Do we need a tmp file? | |
TMPFILE='.cls.tmp' | |
for fname in "$@" | |
do | |
find_classes "$fname" > "$TMPFILE" | |
ref_count "$fname" "$TMPFILE" | sort -r | |
done | |
} | |
function missing_name { | |
echo "gitmine => Missing file name or pattern." | |
echo "For instance:" | |
echo "gitmine refc foo.cs" | |
echo "gitmine refc '*.cs'" | |
exit 1 | |
} | |
function invalid_op { | |
echo "Invalid option" | |
exit 1 | |
} | |
function ref_count { | |
if [[ "$#" -ne 2 || -z $2 || '' = $2 || "''" = $2 ]]; then | |
missing_name | |
fi | |
files=$(bash -c "find '$1' -type f -name $2" | wc -l) | |
if [ $files -eq 0 ]; then | |
echo "There are no files." | |
exit 1 | |
fi | |
echo "gitmine will refcount $files files. This may take a while..." | |
read -p "Do you want to continue (y/n)? " choice | |
case "$choice" in | |
y|Y ) echo "yes";; | |
n|N ) exit;; | |
* ) invalid_op;; | |
esac | |
bash -c "find '$1' -type f -name $2" | \ | |
xargs bash -c 'find_and_count "$@"' _ | \ | |
awk "{ names[$2] += $1; }; | |
END { for(name in names) { | |
printf \"%-6d %s \n\", names[name], name | |
} | |
}" | \ | |
sort -rn | \ | |
less | |
} | |
function print_score { | |
# where $1 points to the author's name. | |
awk "{ names[\$1] += 1; }; | |
END { for (n in names) printf(\"|%8d | %-23s |\n\", names[n], n)}" | |
} | |
function hi { | |
fname="$1" | |
echo "" | |
echo "+-----------------------------------+" | |
echo "| Commits | Author |" | |
echo "+-----------------------------------+" | |
git log --pretty=format:"%an" -- $fname | \ | |
print_score | \ | |
sort -nr | |
echo "+-----------------------------------+" | |
echo "" | |
echo "" | |
} | |
# It shows file names and commits count in descending order, which means that | |
# you get to see the files that change most ofthen first. | |
# It's also possible to specify an author and see commits made | |
# by that person only. | |
function commits_per_file { | |
git log --name-only --author=$1 --pretty=format: |\ | |
sort | uniq -c | sort -nr | less | |
} | |
# commits since until | |
function commits_since_until { | |
if [ "$1" == "" ] || [ "$2" == "" ]; | |
then | |
echo "" | |
echo "Command: Show commits since until." | |
echo "ERR: Arguments <since> and <until> are both required." | |
echo "Tip: You can also filter by <author>." | |
echo "" | |
exit 1 | |
fi | |
# Filter by author? | |
if [ "$3" != "" ]; then | |
git log --since $1 --until $2 \ | |
--pretty=format:"%C(yellow)%h %Creset%ad %C(blue)%an %Creset%s" \ | |
--date=short --author $3 \ | |
--reverse | |
else | |
git log --since $1 --until $2 \ | |
--pretty=format:"%C(yellow)%h %Creset%ad %C(blue)%an %Creset%s" \ | |
--date=short \ | |
--reverse | |
fi | |
} | |
function wbd { | |
if [ "$1" == "" ] || [ "$2" == "" ]; | |
then | |
echo "Command: What been doing?" | |
echo "ERR: Arguments <since> and <author> are both required." | |
echo "" | |
exit 1 | |
fi | |
git log --since $1 --pretty=format:"%C(yellow)%h %Creset%an %ad %s" \ | |
--date=short --author $2 | |
} | |
function show_help { | |
echo "gitmine 0.1" | |
echo "Usage: gitmine <command> [<args>]" | |
echo "" | |
echo "Common commands:" | |
# working | |
echo " cmts <since> <until> [author] Show commits since/until." | |
echo " wbd <since> <author> What been doing?" | |
echo " cmpf [author] Commits per file." | |
echo " hi <filename> Commits count per file." | |
echo " fcf Frequently Changed Files." | |
echo " score Global commit count per author." | |
# FIXME!!! | |
echo " refc <filename> Count class references." | |
echo "" | |
} | |
export -f find_classes | |
export -f ref_count | |
export -f find_and_count | |
export -f missing_name | |
export -f invalid_op | |
export -f print_score | |
export -f wbd | |
export -f commits_per_file | |
export -f hi | |
export -f show_help | |
export -f commits_since_until | |
if [[ "$1" == "cmpf" ]]; then | |
commits_per_file "$2" | |
elif [[ "$1" == "wbd" ]]; then | |
wbd "$2" "$3" | |
elif [[ "$1" == "cmts" ]]; then | |
commits_since_until "$2" "$3" "$4" | |
elif [[ "$1" == "score" ]]; then | |
# Global count. | |
# All authors, all files. | |
hi | |
elif [[ "$1" == "hi" ]]; then | |
# Counts commits per file. | |
# All authors, ONE file. | |
if [ "$2" == "" ]; then | |
echo "The parameter <file> is required for this command."; | |
exit 1 | |
fi | |
hi "$2" | |
elif [[ "$1" == "pwd" ]]; then # <- This one is for debugging purposes. | |
pwd | |
exit | |
elif [[ "$1" == "refc" ]]; then | |
dir="$(pwd)" | |
ref_count "$dir" "'$2'" # <- $2 MUST be quoted to avoid path expansion. | |
elif [[ "$1" == "fcf" ]]; then | |
frequently_changed_files | |
else | |
show_help | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment