Compare commits

...

7 Commits

Author SHA1 Message Date
arzzen
376887a394 update manpage 2025-06-17 19:01:38 +02:00
arzzen
cebb5a8f48 Merge pull request #188 from git-quick-stats/task/issue-116
_MENU_THEME=none
2025-06-17 18:52:01 +02:00
arzzen
95136d3f92 add filter ignored authors 2025-06-16 14:40:49 +02:00
arzzen
79c735c814 fix backslash 2025-06-16 14:36:11 +02:00
arzzen
ec6a95d2ef cleanup 2025-06-16 14:33:03 +02:00
arzzen
b65b100cd8 fix tests 2025-06-16 14:29:24 +02:00
arzzen
dd7719c3b0 sorting contribution stats 2025-06-16 14:03:21 +02:00
4 changed files with 298 additions and 202 deletions

View File

@@ -204,12 +204,22 @@ You can set the variable `_GIT_BRANCH` to set the branch of the stats. Works wit
export _GIT_BRANCH="master" export _GIT_BRANCH="master"
``` ```
### Ignore authors
You can set the variable `_GIT_IGNORE_AUTHORS` to filter out specific authors. It will affect the "All contributors", ""Suggested code reviewers" and "New contributors" options. You can set the variable `_GIT_IGNORE_AUTHORS` to filter out specific authors. It will affect the "All contributors", ""Suggested code reviewers" and "New contributors" options.
```bash ```bash
export _GIT_IGNORE_AUTHORS="(author@examle.com|username)" export _GIT_IGNORE_AUTHORS="(author@examle.com|username)"
``` ```
### Sorting contribution stats
You can sort contribution stats by field `name`, `commits`, `insertions`, `deletions`, or `lines` (total lines changed) and order (`asc`, `desc`). e.g.: `commits-desc`
```bash
export _GIT_SORT_BY="name-asc"
```
### Color themes ### Color themes
You can change to the legacy color scheme by toggling the variable `_MENU_THEME` between `default` and `legacy`. You can change to the legacy color scheme by toggling the variable `_MENU_THEME` between `default` and `legacy`.

View File

@@ -76,6 +76,14 @@ else
_ignore_authors="" _ignore_authors=""
fi fi
# Sort by field and order for contribution stats
_GIT_SORT_BY=${_GIT_SORT_BY:-name-asc}
# If the user has not set a sort order, default to name-asc
if [[ ! "$_GIT_SORT_BY" =~ ^(name|commits|insertions|deletions|lines)-(asc|desc)$ ]]; then
echo "Invalid sort option: $_GIT_SORT_BY. Defaulting to 'name-asc'."
_GIT_SORT_BY="name-asc"
fi
# Default menu theme # Default menu theme
# Set the legacy theme by typing "export _MENU_THEME=legacy" # Set the legacy theme by typing "export _MENU_THEME=legacy"
_theme="${_MENU_THEME:=default}" _theme="${_MENU_THEME:=default}"
@@ -96,36 +104,39 @@ function commitsCalendarByAuthor() {
--date=iso --author="$author" "$_since" "$_until" $_log_options \ --date=iso --author="$author" "$_since" "$_until" $_log_options \
--pretty='%ad' $_pathspec | awk ' --pretty='%ad' $_pathspec | awk '
{ {
split($0, a, " "); split($0, a, " ");
# a[1] = YYYY-MM-DD # a[1] = YYYY-MM-DD
split(a[1], date_fields, "-"); split(a[1], date_fields, "-");
mon = date_fields[2] + 0; mon = date_fields[2] + 0;
cmd = "date -d \"" a[1] "\" +%u"; cmd = "date -d \"" a[1] "\" +%u";
cmd | getline weekday; cmd | getline weekday;
close(cmd); close(cmd);
# weekday: 1=Mon, ..., 7=Sun # weekday: 1=Mon, ..., 7=Sun
count[weekday][mon]++; count[weekday][mon]++;
} }
END { END {
# Output matrix # Output matrix
for (d=1; d<=7; d++) { for (d=1; d<=7; d++) {
if (d==1) printf "Mon "; if (d==1) printf "Mon ";
else if (d==2) printf "Tue "; else if (d==2) printf "Tue ";
else if (d==3) printf "Wed "; else if (d==3) printf "Wed ";
else if (d==4) printf "Thu "; else if (d==4) printf "Thu ";
else if (d==5) printf "Fri "; else if (d==5) printf "Fri ";
else if (d==6) printf "Sat "; else if (d==6) printf "Sat ";
else if (d==7) printf "Sun "; else if (d==7) printf "Sun ";
for (m=1; m<=12; m++) { for (m=1; m<=12; m++) {
c = count[d][m]+0; c = count[d][m]+0;
if (c==0) out="..."; if (c==0)
else if (c<=9) out="░░░"; out="...";
else if (c<=19) out="▒▒▒"; else if (c<=9)
else out="▓▓▓"; out="░░░";
printf "%s%s", out, (m<12?" ":"\n"); else if (c<=19)
} out="▒▒▒";
else
out="▓▓▓";
printf "%s%s", out, (m<12?" ":"\n"); }
} }
printf "\nLegend: ... = 0 ░░░ = 19 ▒▒▒ = 1019 ▓▓▓ = 20+ commits\n"; printf "\nLegend: ... = 0 ░░░ = 19 ▒▒▒ = 1019 ▓▓▓ = 20+ commits\n";
} }
' '
} }
@@ -196,7 +207,7 @@ DESCRIPTION
Any git repository contains tons of information about commits, contributors, Any git repository contains tons of information about commits, contributors,
and files. Extracting this information is not always trivial, mostly because and files. Extracting this information is not always trivial, mostly because
of a gadzillion options to a gadzillion git commands. of a gadzillion options to a gadzillion git commands.
This program allows you to see detailed information about a git repository. This program allows you to see detailed information about a git repository.
GENERATE OPTIONS GENERATE OPTIONS
@@ -270,7 +281,9 @@ ADDITIONAL USAGE
You can set _GIT_BRANCH to set the branch of the stats You can set _GIT_BRANCH to set the branch of the stats
ex: export _GIT_BRANCH=master ex: export _GIT_BRANCH=master
You can set _GIT_IGNORE_AUTHORS to filter out specific authors You can set _GIT_IGNORE_AUTHORS to filter out specific authors
ex: export _GIT_IGNORE_AUTHORS=\"(author1|author2)\"" ex: export _GIT_IGNORE_AUTHORS=\"(author1|author2)\"
You can sort contribution stats by field \"name\", \"commits\", \"insertions\", \"deletions\", or \"lines\" - total lines changed and order - \"asc\", \"desc\"
ex: export _GIT_SORT_BY=\"name-asc\""
} }
################################################################################ ################################################################################
@@ -362,7 +375,7 @@ filter_ignored_authors() {
################################################################################ ################################################################################
# DESC: Shows detailed contribution stats per author by parsing every commit in # DESC: Shows detailed contribution stats per author by parsing every commit in
# the repo and outputting their contribution stats # the repo and outputting their contribution stats.
# ARGS: $branch (optional): Users can specify an alternative branch instead of # ARGS: $branch (optional): Users can specify an alternative branch instead of
# the current default one # the current default one
# OUTS: None # OUTS: None
@@ -394,82 +407,151 @@ function detailedGitStats() {
optionPicked "Contribution stats (by author) on the current branch:" optionPicked "Contribution stats (by author) on the current branch:"
fi fi
local sort_by="${_GIT_SORT_BY:-name-asc}"
local sort_field
local sort_order
local sort_command
sort_field=$(echo "$sort_by" | awk -F- '{print $1}')
sort_order=$(echo "$sort_by" | awk -F- '{print $2}')
local sort_key
case "$sort_field" in
name) sort_key=1 ;;
commits) sort_key=2 ;;
insertions) sort_key=3 ;;
deletions) sort_key=4 ;;
lines) sort_key=5 ;;
*)
echo "Invalid sort field: $sort_field. Defaulting to 'name'."
sort_key=1
;;
esac
echo -e "Sorting by: $sort_field ($sort_order)\n"
local sort_flags="-t'|' -k${sort_key},${sort_key}"
if [[ "$sort_field" != "name" ]]; then
sort_flags="${sort_flags}n"
fi
if [[ "$sort_order" == "desc" ]]; then
sort_flags="${sort_flags}r"
fi
sort_command="sort ${sort_flags}"
# 1. git log -> awk (extract data) -> sort -> awk (format output with graphs)
git -c log.showSignature=false log ${_branch} --use-mailmap $_merges --numstat \ git -c log.showSignature=false log ${_branch} --use-mailmap $_merges --numstat \
--pretty="format:commit %H%nAuthor: %aN <%aE>%nDate: %ad%n%n%w(0,4,4)%B%n" \ --pretty="format:commit %H%nAuthor: %aN <%aE>%nDate: %ad%n%n%w(0,4,4)%B%n" \
"$_since" "$_until" $_log_options $_pathspec | LC_ALL=C awk ' "$_since" "$_until" $_log_options $_pathspec |
function printStats(author) { LC_ALL=C awk '
printf "\t%s:\n", author # This first awk script extracts raw data into a delimited format
if(more["total"] > 0) {
printf "\t insertions: %d\t(%.0f%%)\n", more[author], \
(more[author] / more["total"] * 100)
} else {
printf "\t insertions: %d\t(%.0f%%)\n", 0, 0
}
if(less["total"] > 0) {
printf "\t deletions: %d\t(%.0f%%)\n", less[author], \
(less[author] / less["total"] * 100)
} else {
printf "\t deletions: %d\t(%.0f%%)\n", 0, 0
}
if(file["total"] > 0) {
printf "\t files: %d\t(%.0f%%)\n", file[author], \
(file[author] / file["total"] * 100)
}
if(commits["total"] > 0) {
printf "\t commits: %d\t(%.0f%%)\n", commits[author], \
(commits[author] / commits["total"] * 100)
}
if (first[author] != "") {
if ( ((more["total"] + less["total"]) * 100) > 0) {
printf "\t lines changed: %d\t", more[author] + less[author]
printf "(%.0f%%)\n", ((more[author] + less[author]) / \
(more["total"] + less["total"]) * 100)
}
else {
printf "\t lines changed: %d\t(0%%)\n", (more[author] + less[author])
}
printf "\t first commit: %s\n", first[author]
printf "\t last commit: %s\n", last[author]
}
printf "\n"
}
/^Author:/ { /^Author:/ {
$1 = "" $1 = ""; author = $0;
author = $0 commits[author] += 1;
commits[author] += 1
commits["total"] += 1
} }
/^Date:/ { /^Date:/ {
$1=""; $1=""; current_date = substr($0, 2);
first[author] = substr($0, 2) if (last[author] == "") { last[author] = current_date; }
if(last[author] == "" ) { last[author] = first[author] } first[author] = current_date;
} }
/^[0-9]/ { /^[0-9]/ {
more[author] += $1 more[author] += $1;
less[author] += $2 less[author] += $2;
file[author] += 1;
file[author] += 1
more["total"] += $1
less["total"] += $2
file["total"] += 1
} }
END { END {
for (author in commits) { for (author in commits) {
if (author != "total") { lines_changed = more[author] + less[author];
printStats(author) printf "%s|%d|%d|%d|%d|%d|%s|%s\n",
author, commits[author]+0, more[author]+0, less[author]+0,
lines_changed, file[author]+0, first[author], last[author];
} }
}' |
eval "$sort_command" | filter_ignored_authors |
LC_ALL=C awk '
# This second awk script stores data, calculates totals, and then formats output with graphs
BEGIN {
FS = "|";
total_commits = 0; total_insertions = 0; total_deletions = 0;
total_lines = 0; total_files = 0;
num_authors = 0; # Counter for stored authors
} }
printStats("total") {
# Store all data for a second pass after totals are known
authors[num_authors] = $1;
commits_arr[num_authors] = $2;
insertions_arr[num_authors] = $3;
deletions_arr[num_authors] = $4;
lines_changed_arr[num_authors] = $5;
files_arr[num_authors] = $6;
first_commit_arr[num_authors] = $7;
last_commit_arr[num_authors] = $8;
# Accumulate overall totals
total_commits += $2;
total_insertions += $3;
total_deletions += $4;
total_lines += $5;
total_files += $6;
num_authors++;
}
END {
for (j = 0; j < num_authors; j++) {
author = authors[j];
current_commits = commits_arr[j];
current_insertions = insertions_arr[j];
current_deletions = deletions_arr[j];
current_lines_changed = lines_changed_arr[j];
current_files = files_arr[j];
current_first_commit = first_commit_arr[j];
current_last_commit = last_commit_arr[j];
printf "\t%s:\n", author;
# Commits graph
if (total_commits > 0) {
commit_percent = (current_commits * 100.0) / total_commits;
printf "\t commits: %d (%.1f%%)\n", current_commits, commit_percent;
} else {
printf "\t commits: %d\n", current_commits;
}
# Insertions graph
if (total_insertions > 0) {
insert_percent = (current_insertions * 100.0) / total_insertions;
printf "\t insertions: %d (%.1f%%)\n", current_insertions, insert_percent;
} else {
printf "\t insertions: %d\n", current_insertions;
}
# Deletions graph
if (total_deletions > 0) {
delete_percent = (current_deletions * 100.0) / total_deletions;
printf "\t deletions: %d (%.1f%%)\n", current_deletions, delete_percent;
} else {
printf "\t deletions: %d\n", current_deletions;
}
# Lines changed graph
if (total_lines > 0) {
lines_percent = (current_lines_changed * 100.0) / total_lines;
printf "\t lines changed: %d (%.1f%%)\n", current_lines_changed, lines_percent;
} else {
printf "\t lines changed: %d\n", current_lines_changed;
}
printf "\t files: %d\n", current_files;
printf "\t first commit: %s\n", current_first_commit;
printf "\t last commit: %s\n\n", current_last_commit;
}
# Print overall totals
printf "\t%s:\n", " total";
printf "\t commits: %d\n", total_commits;
printf "\t insertions: %d\n", total_insertions;
printf "\t deletions: %d\n", total_deletions;
printf "\t lines changed: %d\n", total_lines;
printf "\t files: %d\n\n", total_files;
}' }'
} }
@@ -554,9 +636,10 @@ function csvOutput() {
local _branch="" local _branch=""
# Check if requesting for a specific branch # Check if requesting for a specific branch
if [[ -n "${branch}" ]]; then if [[ -n "${branch}" ]]; then
# Check if branch exist # Check if branch exist
if [[ $(git show-ref refs/heads/"${branch}") ]] ; then if [[ $(git show-ref refs/heads/"${branch}") ]] ;
then
is_branch_existing=true is_branch_existing=true
_branch="${branch}" _branch="${branch}"
else else
@@ -671,7 +754,7 @@ function toJsonProp() {
# add in hold buffer and loop while the string is not finished. # add in hold buffer and loop while the string is not finished.
/^'"$propTag"',?\r?$/ ! { H; n; b eos; } /^'"$propTag"',?\r?$/ ! { H; n; b eos; }
# end of the string, flip buffer to current pattern. # end of the string, flip buffer to current pattern.
# keeps the comma if any, or a space as an empty placeholder. # keeps the comma if any, or a space as an empty placeholder.
/,\r?$/ ! { x; s/\r?$/ / } /,\r?$/ ! { x; s/\r?$/ / }
/,\r?$/ { x; s/\r?$/,/ } /,\r?$/ { x; s/\r?$/,/ }
} }
@@ -891,7 +974,8 @@ function commitsByYear() {
echo -e "\tyear\tsum" echo -e "\tyear\tsum"
# Add time strings to make these a touch more robust # Add time strings to make these a touch more robust
for year in $(seq "$startYear" "$endYear"); do for year in $(seq "$startYear" "$endYear");
do
if [[ "$year" = "$startYear" ]]; then if [[ "$year" = "$startYear" ]]; then
__since=$_since __since=$_since
__until="--until=$year-12-31 23:59:59" __until="--until=$year-12-31 23:59:59"
@@ -918,13 +1002,12 @@ function commitsByYear() {
} }
END{ END{
for (year in count) { for (year in count) {
s="|"; s="|";
if (total > 0) { if (total > 0) {
percent = ((count[year] / total) * 100) / 1.25; percent = ((count[year] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
for (i = 1; i <= percent; ++i) {
s=s"█" s=s"█"
} }
printf( "\t%s\t%-0s\t%s\n", year, count[year], s ); printf( "\t%s\t%-0s\t%s\n", year, count[year], s );
} }
} }
}' | sort }' | sort
@@ -951,13 +1034,12 @@ function commitsByMonth() {
} }
END{ END{
for (month in count) { for (month in count) {
s="|"; s="|";
if (total > 0) { if (total > 0) {
percent = ((count[month] / total) * 100) / 1.25; percent = ((count[month] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
for (i = 1; i <= percent; ++i) {
s=s"█" s=s"█"
} }
printf( "\t%s\t%-0s\t%s\n", month, count[month], s ); printf( "\t%s\t%-0s\t%s\n", month, count[month], s );
} }
} }
}' | LC_TIME="en_EN.UTF-8" sort -M }' | LC_TIME="en_EN.UTF-8" sort -M
@@ -986,25 +1068,22 @@ function commitsByWeekday() {
do do
echo -en "\t$counter\t$i\t" echo -en "\t$counter\t$i\t"
git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \
"${_author}" "$_since" "$_until" $_log_options | "${_author}" "$_since" "$_until" $_log_options | grep -cE "^ * $i \w\w\w [0-9]{1,2} " || continue
grep -cE "^ * $i \w\w\w [0-9]{1,2} " || continue
counter=$((counter+1)) counter=$((counter+1))
done | awk '{ done | awk '{
} }
NR == FNR { NR == FNR {
count[$1" "$2] = $3; count[$1" "$2] = $3; total += $3;
total += $3;
next next
} }
END{ END{
for (day in count) { for (day in count) {
s="|"; s="|"; if (total > 0) {
if (total > 0) { percent = ((count[day] / total) * 100) / 1.25;
percent = ((count[day] / total) * 100) / 1.25;
for (i = 1; i <= percent; ++i) { for (i = 1; i <= percent; ++i) {
s=s"█" s=s"█"
} }
printf("\t%s\t%s\t%-0s\t%s\n", substr(day,0,1), substr(day,3,5), count[day], s); printf("\t%s\t%s\t%-0s\t%s\n", substr(day,0,1), substr(day,3,5), count[day], s);
} }
} }
}' | sort -k 1 -n | awk '{$1=""}1' | awk '{$1=$1}1' \ }' | sort -k 1 -n | awk '{$1=""}1' | awk '{$1=$1}1' \
@@ -1034,21 +1113,19 @@ function commitsByHour() {
do do
echo -ne "\t$i\t" echo -ne "\t$i\t"
git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \
"${_author}" "$_since" "$_until" $_log_options | "${_author}" "$_since" "$_until" $_log_options | grep -cE '[0-9] '$i':[0-9]' || continue
grep -cE '[0-9] '$i':[0-9]' || continue
done | awk '{ done | awk '{
count[$1] = $2 count[$1] = $2
total += $2 total += $2
} }
END{ END{
for (hour in count) { for (hour in count) {
s="|"; s="|";
if (total > 0) { if (total > 0) {
percent = ((count[hour] / total) * 100) / 1.25; percent = ((count[hour] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
for (i = 1; i <= percent; ++i) {
s=s"█" s=s"█"
} }
printf( "\t%s\t%-0s\t%s\n", hour, count[hour], s ); printf( "\t%s\t%-0s\t%s\n", hour, count[hour], s );
} }
} }
}' | sort }' | sort
@@ -1111,7 +1188,8 @@ if ! git rev-parse --is-inside-work-tree > /dev/null; then
exit 1 exit 1
fi fi
# Parse non-interative commands
# Parse non-interactive commands
if [[ "$#" -eq 1 ]]; then if [[ "$#" -eq 1 ]]; then
case "$1" in case "$1" in
# GENERATE OPTIONS # GENERATE OPTIONS
@@ -1207,95 +1285,97 @@ if [[ "$#" -eq 1 ]]; then
# SUGGEST OPTIONS # SUGGEST OPTIONS
-r|--suggest-reviewers) suggestReviewers;; -r|--suggest-reviewers) suggestReviewers;;
-h|-\?|--help) usage;; -h|-\?|--help) usage;;
*) echo "Invalid argument"; usage; exit 1;; *) echo "Invalid argument: $1"; usage; exit 1;;
esac esac
exit 0; exit 0;
fi fi
[[ "$#" -gt 1 ]] && { echo "Invalid arguments"; usage; exit 1; } [[ "$#" -gt 1 ]] && { echo "Invalid arguments"; usage; exit 1; }
# Parse interactive commands # If no args, run interactive mode
clear if [[ "$#" -eq 0 ]]; then
showMenu # Parse interactive commands
while [[ "${opt}" != "" ]]; do
clear clear
case "${opt}" in showMenu
1) detailedGitStats; showMenu;;
2) branch="" while [[ "${opt}" != "" ]]; do
while [[ -z "${branch}" ]]; do clear
read -r -p "Which branch? " branch case "${opt}" in
done 1) detailedGitStats; showMenu;;
detailedGitStats "${branch}"; showMenu;; 2) branch=""
3) changelogs; showMenu;; while [[ -z "${branch}" ]]; do
4) author="" read -r -p "Which branch? " branch
while [[ -z "${author}" ]]; do done
read -r -p "Which author? " author detailedGitStats "${branch}"; showMenu;;
done 3) changelogs; showMenu;;
changelogs "${author}"; showMenu;; 4) author=""
5) myDailyStats; showMenu;; while [[ -z "${author}" ]]; do
6) branch="" read -r -p "Which author? " author
while [[ -z "${branch}" ]]; do done
read -r -p "Which branch? " branch changelogs "${author}"; showMenu;;
done 5) myDailyStats; showMenu;;
csvOutput "${branch}"; showMenu;; 6) branch=""
7) json_path="" while [[ -z "${branch}" ]]; do
while [[ -z "${json_path}" ]]; do read -r -p "Which branch? " branch
echo "NOTE: This feature is in beta!" done
echo "The file name will be saved as \"output.json\"." csvOutput "${branch}"; showMenu;;
echo "The full path must be provided." 7) json_path=""
echo "Variables, subshell commands, or shorthands such as ~ may not be valid." while [[ -z "${json_path}" ]]; do
echo "You do not need the final slash at the end of a directory path." echo "NOTE: This feature is in beta!"
echo "You must have write permission to the folder you are trying to save this to." echo "The file name will be saved as \"output.json\"."
echo "This feature only works interactively and cannot be combined with other options." echo "The full path must be provided."
echo -e "Example of a valid path: /home/$(whoami)\n" echo "Variables, subshell commands, or shorthands such as ~ may not be valid."
read -r -p "Please provide the full path to directory to save JSON file: " json_path echo "You do not need the final slash at the end of a directory path."
if [[ ! -w "${json_path}" ]]; then echo "You must have write permission to the folder you are trying to save this to."
echo "Invalid path or permission denied to write to given area." echo "This feature only works interactively and cannot be combined with other options."
json_path="" echo -e "Example of a valid path: /home/$(whoami)\n"
fi read -r -p "Please provide the full path to directory to save JSON file: " json_path
done if [[ ! -w "${json_path}" ]]; then
jsonOutput "${json_path}"; showMenu;; echo "Invalid path or permission denied to write to given area."
8) branchTree; showMenu;; json_path=""
9) branchesByDate; showMenu;; fi
10) contributors; showMenu;; done
11) newDate="" jsonOutput "${json_path}"; showMenu;;
while [[ -z "${newDate}" ]]; do 8) branchTree; showMenu;;
read -r -p "Since what date? (e.g. '2023-04-13', '13 April 2023', 'last Thursday') " newDate 9) branchesByDate; showMenu;;
# Test if the date provide is valid and try again if it isn't. 10) contributors; showMenu;;
if ! date -d "${newDate}" +%s > /dev/null 2>&1; then 11) newDate=""
newDate="" while [[ -z "${newDate}" ]]; do
fi read -r -p "Since what date? (e.g. '2023-04-13', '13 April 2023', 'last Thursday') " newDate
done if ! date -d "${newDate}" +%s > /dev/null 2>&1; then
newContributors "${newDate}"; showMenu;; newDate=""
12) commitsPerAuthor; showMenu;; fi
13) commitsPerDay; showMenu;; done
14) commitsByMonth; showMenu;; newContributors "${newDate}"; showMenu;;
15) commitsByYear; showMenu;; 12) commitsPerAuthor; showMenu;;
16) commitsByWeekday; showMenu;; 13) commitsPerDay; showMenu;;
17) author="" 14) commitsByMonth; showMenu;;
while [[ -z "${author}" ]]; do 15) commitsByYear; showMenu;;
read -r -p "Which author? " author 16) commitsByWeekday; showMenu;;
done 17) author=""
commitsByWeekday "${author}"; showMenu;; while [[ -z "${author}" ]]; do
18) commitsByHour; showMenu;; read -r -p "Which author? " author
19) author="" done
while [[ -z "${author}" ]]; do commitsByWeekday "${author}"; showMenu;;
read -r -p "Which author? " author 18) commitsByHour; showMenu;;
done 19) author=""
commitsByHour "${author}"; showMenu;; while [[ -z "${author}" ]]; do
20) commitsByTimezone; showMenu;; read -r -p "Which author? " author
21) author="" done
while [[ -z "${author}" ]]; do commitsByHour "${author}"; showMenu;;
read -r -p "Which author? " author 20) commitsByTimezone; showMenu;;
done 21) author=""
commitsByTimezone "${author}"; showMenu;; while [[ -z "${author}" ]]; do
22) suggestReviewers; showMenu;; read -r -p "Which author? " author
23) author="" done
while [[ -z "${author}" ]]; do commitsByTimezone "${author}"; showMenu;;
read -r -p "Which author? " author 22) suggestReviewers; showMenu;;
done 23) author=""
commitsCalendarByAuthor "${author}"; showMenu;; while [[ -z "${author}" ]]; do
q|"\n") exit;; read -r -p "Which author? " author
*) clear; optionPicked "Pick an option from the menu"; showMenu;; done
esac commitsCalendarByAuthor "${author}"; showMenu;;
done q|"\n") exit;;
*) clear; optionPicked "Pick an option from the menu"; showMenu;;
esac
done
fi

View File

@@ -165,7 +165,11 @@ You can also set _GIT_MERGE_VIEW to only show merge commits, example:
.PP .PP
.B export _GIT_MERGE_VIEW="exclusive" .B export _GIT_MERGE_VIEW="exclusive"
.PP .PP
You can change to the legacy color scheme by toggling the variable `_MENU_THEME` between `default` and `legacy`. You can completely disable the color theme by setting the `_MENU_THEME` variable to `none`, example: You can sort contribution stats by field "name", "commits", "insertions", "deletions", or "lines" (total lines changed) and order ("asc", "desc"). e.g.: "commits-desc"
.PP
.B export _GIT_SORT_BY="name-asc"
.PP
You can change to the legacy color scheme by toggling the variable "_MENU_THEME" between "default" and "legacy". You can completely disable the color theme by setting the "_MENU_THEME" variable to "none", example:
.PP .PP
.B export _MENU_THEME=legacy .B export _MENU_THEME=legacy
.PP .PP

View File

@@ -11,7 +11,7 @@
. tests/assert.sh -v . tests/assert.sh -v
src="./git-quick-stats" src="./git-quick-stats"
assert "$src fail" "Invalid argument assert "$src fail" "Invalid argument: fail
NAME NAME
git-quick-stats - Simple and efficient way to access various stats in a git repo git-quick-stats - Simple and efficient way to access various stats in a git repo
@@ -24,7 +24,7 @@ DESCRIPTION
Any git repository contains tons of information about commits, contributors, Any git repository contains tons of information about commits, contributors,
and files. Extracting this information is not always trivial, mostly because and files. Extracting this information is not always trivial, mostly because
of a gadzillion options to a gadzillion git commands. of a gadzillion options to a gadzillion git commands.
This program allows you to see detailed information about a git repository. This program allows you to see detailed information about a git repository.
GENERATE OPTIONS GENERATE OPTIONS
@@ -98,14 +98,16 @@ ADDITIONAL USAGE
You can set _GIT_BRANCH to set the branch of the stats You can set _GIT_BRANCH to set the branch of the stats
ex: export _GIT_BRANCH=master ex: export _GIT_BRANCH=master
You can set _GIT_IGNORE_AUTHORS to filter out specific authors You can set _GIT_IGNORE_AUTHORS to filter out specific authors
ex: export _GIT_IGNORE_AUTHORS=\"(author1|author2)\"" ex: export _GIT_IGNORE_AUTHORS=\"(author1|author2)\"
You can sort contribution stats by field \"name\", \"commits\", \"insertions\", \"deletions\", or \"lines\" - total lines changed and order - \"asc\", \"desc\"
ex: export _GIT_SORT_BY=\"name-asc\""
assert_raises "$src fail" 1 assert_raises "$src fail" 1
assert_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)" assert_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)"
assert_success "$src --suggest-reviewers" assert_success "$src --suggest-reviewers"
assert_contains "$src --detailed-git-stats" "Contribution stats" assert_contains "$src --detailed-git-stats" "Contribution stats (by author) on the current branch"
assert_success "$src --detailed-git-stats" assert_success "$src --detailed-git-stats"
assert_contains "$src --commits-per-day" "Git commits per date" assert_contains "$src --commits-per-day" "Git commits per date"