Compare commits

..

20 Commits

Author SHA1 Message Date
arzzen
748576b541 citation file 2025-06-22 20:03:38 +02:00
arzzen
08c0ca1d85 Update README.md 2025-06-17 19:24:24 +02:00
arzzen
e8843da10e Merge pull request #189 from git-quick-stats/134-feature-request-sorting-contribution-stats
Sorting contribution stats
2025-06-17 19:03:55 +02:00
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
343351b762 update manpage 2025-06-17 18:49:38 +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
arzzen
c34ceb4c0e _MENU_THEME=none 2025-06-16 08:55:00 +02:00
arzzen
850b7453eb Merge pull request #187 from FloezeTv/fix/default-until-date-locale 2025-06-15 15:32:51 +02:00
FloezeTv
5063d1aad7 Fix default until-date in other locales
The default until-date used `date` with the system's default locale
as `date` produces localized output by default.
However, `date -d` cannot parse this localized output
but requires a locale independent format:
https://www.gnu.org/software/coreutils/manual/html_node/Options-for-date.html#index-_002dd-19
This sets the locale to a format parsable by `date` as in the article.
2025-06-15 14:03:10 +02:00
arzzen
53b9971cf7 fix tests 2025-06-15 13:30:16 +02:00
arzzen
eec6d53ce9 Merge pull request #183 from git-quick-stats/feat/activity-calendar
Activity calendar by author
2025-06-14 19:04:01 +02:00
arzzen
d06a7ca090 Merge branch 'master' into feat/activity-calendar 2025-06-14 19:03:52 +02:00
arzzen
462439a855 Merge pull request #184 from git-quick-stats/task/ignore-authors
Ignore authors #141
2025-06-14 19:02:48 +02:00
arzzen
d4f71cfdad removed unuse arrays 2025-06-14 14:43:14 +02:00
arzzen
e1e02024a1 Activity calendar by author 2025-06-08 19:39:29 +02:00
5 changed files with 442 additions and 202 deletions

25
CITATION.cff Normal file
View File

@@ -0,0 +1,25 @@
cff-version: 1.2.0
title: Git Quick Stats
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- name-particle: tomice
- name-particle: arzzen
identifiers:
- type: url
value: "https://github.com/git-quick-stats"
description: >-
Simple and efficient way to access various statistics
in a git repository
repository-code: "https://github.com/git-quick-stats/git-quick-stats"
url: "https://git-quick-stats.sh/"
abstract: >-
Git-quick-stats is a simple and efficient way to access
various statistics in a git repository.
keywords:
- git
- stats
license: MIT
version: 2.6.2

View File

@@ -6,7 +6,8 @@
>
> Any git repository may contain tons of information about commits, contributors, and files. Extracting this information is not always trivial, mostly because there are a gadzillion options to a gadzillion git commands - I don't think there is a single person alive who knows them all. Probably not even [Linus Torvalds](https://github.com/torvalds) himself :).
![mainMenuScreenshot](https://github.com/user-attachments/assets/05a20ff1-44f6-4e44-9d62-c2c089f9ff4e)
![mainImageScreenshot](https://github.com/user-attachments/assets/7d8637a4-5a67-49f6-8724-ca7548b987c6)
## Table of Contents
@@ -54,9 +55,12 @@
## Screenshots
![commitsByWeekdayScreenshot](https://github.com/user-attachments/assets/3a55f3ac-8801-4bbf-9b3a-92b53a64631e)
![commitsByWeekdayScreenshot](https://github.com/user-attachments/assets/3496d245-6385-47d1-878a-726e79100eb1)
![commitsByHourScreenshot](https://github.com/user-attachments/assets/9f1d69d9-46e0-411d-a5ed-905ffdfb887a)
![commitActivityScreenshot](https://github.com/user-attachments/assets/693fff31-65c7-4b9f-a011-6114a2d10a26)
![commitsByHourScreenshot](https://github.com/user-attachments/assets/d7de5280-8bb9-4391-9c6c-7e688f2df171)
## Usage
@@ -204,18 +208,31 @@ You can set the variable `_GIT_BRANCH` to set the branch of the stats. Works wit
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.
```bash
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
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`.
You can completely disable the color theme by setting the `_MENU_THEME` variable to `none`.
```bash
export _MENU_THEME="legacy"
# or
export _MENU_THEME="none"
```
![legacyThemeScreenshot](https://github.com/user-attachments/assets/3b319c1a-827f-47b8-bbfa-b8b59a39deef)

View File

@@ -27,7 +27,7 @@ _until=${_GIT_UNTIL:-}
if [[ -n "${_until}" ]]; then
_until="--until=$_until"
else
_until="--until=$(date '+%a, %d %b %Y %H:%M:%S %Z')"
_until="--until=$(LC_TIME=C date '+%a, %d %b %Y %H:%M:%S %Z')"
fi
# Set files or directories to be excluded in stats
@@ -76,10 +76,71 @@ else
_ignore_authors=""
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
# Set the legacy theme by typing "export _MENU_THEME=legacy"
_theme="${_MENU_THEME:=default}"
# DESC: Shows a calendar heatmap of commits per day-of-week per month for a given author
# ARGS: $author (required)
function commitsCalendarByAuthor() {
local author="${1:-}"
[[ -z "$author" ]] && { echo "Usage: commitsCalendarByAuthor <author>"; return 1; }
optionPicked "Commit Activity Calendar for '$author'"
# Print header
printf "\n Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n"
# Gather commit counts
git -c log.showSignature=false log --use-mailmap $_merges \
--date=iso --author="$author" "$_since" "$_until" $_log_options \
--pretty='%ad' $_pathspec | awk '
{
split($0, a, " ");
# a[1] = YYYY-MM-DD
split(a[1], date_fields, "-");
mon = date_fields[2] + 0;
cmd = "date -d \"" a[1] "\" +%u";
cmd | getline weekday;
close(cmd);
# weekday: 1=Mon, ..., 7=Sun
count[weekday][mon]++;
}
END {
# Output matrix
for (d=1; d<=7; d++) {
if (d==1) printf "Mon ";
else if (d==2) printf "Tue ";
else if (d==3) printf "Wed ";
else if (d==4) printf "Thu ";
else if (d==5) printf "Fri ";
else if (d==6) printf "Sat ";
else if (d==7) printf "Sun ";
for (m=1; m<=12; m++) {
c = count[d][m]+0;
if (c==0)
out="...";
else if (c<=9)
out="░░░";
else if (c<=19)
out="▒▒▒";
else
out="▓▓▓";
printf "%s%s", out, (m<12?" ":"\n"); }
}
printf "\nLegend: ... = 0 ░░░ = 19 ▒▒▒ = 1019 ▓▓▓ = 20+ commits\n";
}
'
}
################################################################################
# HELPER AND MENU FUNCTIONS
@@ -146,7 +207,7 @@ DESCRIPTION
Any git repository contains tons of information about commits, contributors,
and files. Extracting this information is not always trivial, mostly because
of a gadzillion options to a gadzillion git commands.
This program allows you to see detailed information about a git repository.
GENERATE OPTIONS
@@ -214,12 +275,15 @@ ADDITIONAL USAGE
ex: export _GIT_MERGE_VIEW=enable
You can also set _GIT_MERGE_VIEW to only show merge commits
ex: export _GIT_MERGE_VIEW=exclusive
You can set _MENU_THEME to display the legacy color scheme
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\".
ex: export _MENU_THEME=legacy
You can set _GIT_BRANCH to set the branch of the stats
ex: export _GIT_BRANCH=master
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\""
}
################################################################################
@@ -242,19 +306,29 @@ function showMenu() {
EXIT_TXT=""
# Adjustable color menu option
if [[ "${_theme}" == "legacy" ]]; then
TITLES="${BOLD}${RED}"
TEXT="${NORMAL}${CYAN}"
NUMS="${BOLD}${YELLOW}"
HELP_TXT="${NORMAL}${YELLOW}"
EXIT_TXT="${BOLD}${RED}"
else
TITLES="${BOLD}${CYAN}"
TEXT="${NORMAL}${WHITE}"
NUMS="${NORMAL}${BOLD}${WHITE}"
HELP_TXT="${NORMAL}${CYAN}"
EXIT_TXT="${BOLD}${CYAN}"
fi
case "${_theme}" in
"legacy" )
TITLES="${BOLD}${RED}"
TEXT="${NORMAL}${CYAN}"
NUMS="${BOLD}${YELLOW}"
HELP_TXT="${NORMAL}${YELLOW}"
EXIT_TXT="${BOLD}${RED}"
;;
"none" )
TITLES="${BOLD}"
TEXT="${NORMAL}"
NUMS="${BOLD}"
HELP_TXT="${NORMAL}"
EXIT_TXT="${BOLD}"
;;
*)
TITLES="${BOLD}${CYAN}"
TEXT="${NORMAL}${WHITE}"
NUMS="${NORMAL}${BOLD}${WHITE}"
HELP_TXT="${NORMAL}${CYAN}"
EXIT_TXT="${BOLD}${CYAN}"
;;
esac
printf %b "\\n${TITLES} Generate:${NORMAL}\\n"
printf %b "${NUMS} 1)${TEXT} Contribution stats (by author)\\n"
@@ -281,6 +355,8 @@ function showMenu() {
printf %b "${NUMS} 21)${TEXT} Git commits per timezone by author\\n"
printf %b "\\n${TITLES} Suggest:\\n"
printf %b "${NUMS} 22)${TEXT} Code reviewers (based on git history)\\n"
printf %b "\\n${TITLES} Calendar:\\n"
printf %b "${NUMS} 23)${TEXT} Activity calendar by author\\n"
printf %b "\\n${HELP_TXT}Please enter a menu option or ${EXIT_TXT}press Enter to exit.\\n"
printf %b "${TEXT}> ${NORMAL}"
read -r opt
@@ -299,7 +375,7 @@ filter_ignored_authors() {
################################################################################
# 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
# the current default one
# OUTS: None
@@ -331,82 +407,151 @@ function detailedGitStats() {
optionPicked "Contribution stats (by author) on the current branch:"
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 \
--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 '
function printStats(author) {
printf "\t%s:\n", author
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"
}
"$_since" "$_until" $_log_options $_pathspec |
LC_ALL=C awk '
# This first awk script extracts raw data into a delimited format
/^Author:/ {
$1 = ""
author = $0
commits[author] += 1
commits["total"] += 1
$1 = ""; author = $0;
commits[author] += 1;
}
/^Date:/ {
$1="";
first[author] = substr($0, 2)
if(last[author] == "" ) { last[author] = first[author] }
$1=""; current_date = substr($0, 2);
if (last[author] == "") { last[author] = current_date; }
first[author] = current_date;
}
/^[0-9]/ {
more[author] += $1
less[author] += $2
file[author] += 1
more["total"] += $1
less["total"] += $2
file["total"] += 1
more[author] += $1;
less[author] += $2;
file[author] += 1;
}
END {
for (author in commits) {
if (author != "total") {
printStats(author)
for (author in commits) {
lines_changed = more[author] + less[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;
}'
}
@@ -491,9 +636,10 @@ function csvOutput() {
local _branch=""
# Check if requesting for a specific branch
if [[ -n "${branch}" ]]; then
if [[ -n "${branch}" ]]; then
# Check if branch exist
if [[ $(git show-ref refs/heads/"${branch}") ]] ; then
if [[ $(git show-ref refs/heads/"${branch}") ]] ;
then
is_branch_existing=true
_branch="${branch}"
else
@@ -608,7 +754,7 @@ function toJsonProp() {
# add in hold buffer and loop while the string is not finished.
/^'"$propTag"',?\r?$/ ! { H; n; b eos; }
# 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?$/,/ }
}
@@ -828,7 +974,8 @@ function commitsByYear() {
echo -e "\tyear\tsum"
# 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
__since=$_since
__until="--until=$year-12-31 23:59:59"
@@ -855,13 +1002,12 @@ function commitsByYear() {
}
END{
for (year in count) {
s="|";
s="|";
if (total > 0) {
percent = ((count[year] / total) * 100) / 1.25;
for (i = 1; i <= percent; ++i) {
percent = ((count[year] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
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
@@ -888,13 +1034,12 @@ function commitsByMonth() {
}
END{
for (month in count) {
s="|";
s="|";
if (total > 0) {
percent = ((count[month] / total) * 100) / 1.25;
for (i = 1; i <= percent; ++i) {
percent = ((count[month] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
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
@@ -923,25 +1068,22 @@ function commitsByWeekday() {
do
echo -en "\t$counter\t$i\t"
git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \
"${_author}" "$_since" "$_until" $_log_options |
grep -cE "^ * $i \w\w\w [0-9]{1,2} " || continue
"${_author}" "$_since" "$_until" $_log_options | grep -cE "^ * $i \w\w\w [0-9]{1,2} " || continue
counter=$((counter+1))
done | awk '{
}
NR == FNR {
count[$1" "$2] = $3;
total += $3;
count[$1" "$2] = $3; total += $3;
next
}
END{
for (day in count) {
s="|";
if (total > 0) {
percent = ((count[day] / total) * 100) / 1.25;
s="|"; if (total > 0) {
percent = ((count[day] / total) * 100) / 1.25;
for (i = 1; i <= percent; ++i) {
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' \
@@ -971,21 +1113,19 @@ function commitsByHour() {
do
echo -ne "\t$i\t"
git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \
"${_author}" "$_since" "$_until" $_log_options |
grep -cE '[0-9] '$i':[0-9]' || continue
"${_author}" "$_since" "$_until" $_log_options | grep -cE '[0-9] '$i':[0-9]' || continue
done | awk '{
count[$1] = $2
total += $2
}
END{
for (hour in count) {
s="|";
s="|";
if (total > 0) {
percent = ((count[hour] / total) * 100) / 1.25;
for (i = 1; i <= percent; ++i) {
percent = ((count[hour] / total) * 100) / 1.25; for (i = 1; i <= percent; ++i) {
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
@@ -1048,7 +1188,8 @@ if ! git rev-parse --is-inside-work-tree > /dev/null; then
exit 1
fi
# Parse non-interative commands
# Parse non-interactive commands
if [[ "$#" -eq 1 ]]; then
case "$1" in
# GENERATE OPTIONS
@@ -1134,93 +1275,107 @@ if [[ "$#" -eq 1 ]]; then
read -r -p "Which author? " author
done
commitsByTimezone "${author}";;
# ACTIVITY OPTIONS
-k|--commits-calendar-by-author)
author="${_GIT_AUTHOR:-}"
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsCalendarByAuthor "${author}";;
# SUGGEST OPTIONS
-r|--suggest-reviewers) suggestReviewers;;
-h|-\?|--help) usage;;
*) echo "Invalid argument"; usage; exit 1;;
*) echo "Invalid argument: $1"; usage; exit 1;;
esac
exit 0;
fi
[[ "$#" -gt 1 ]] && { echo "Invalid arguments"; usage; exit 1; }
# Parse interactive commands
clear
showMenu
while [[ "${opt}" != "" ]]; do
# If no args, run interactive mode
if [[ "$#" -eq 0 ]]; then
# Parse interactive commands
clear
case "${opt}" in
1) detailedGitStats; showMenu;;
2) branch=""
while [[ -z "${branch}" ]]; do
read -r -p "Which branch? " branch
done
detailedGitStats "${branch}"; showMenu;;
3) changelogs; showMenu;;
4) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
changelogs "${author}"; showMenu;;
5) myDailyStats; showMenu;;
6) branch=""
while [[ -z "${branch}" ]]; do
read -r -p "Which branch? " branch
done
csvOutput "${branch}"; showMenu;;
7) json_path=""
while [[ -z "${json_path}" ]]; do
echo "NOTE: This feature is in beta!"
echo "The file name will be saved as \"output.json\"."
echo "The full path must be provided."
echo "Variables, subshell commands, or shorthands such as ~ may not be valid."
echo "You do not need the final slash at the end of a directory path."
echo "You must have write permission to the folder you are trying to save this to."
echo "This feature only works interactively and cannot be combined with other options."
echo -e "Example of a valid path: /home/$(whoami)\n"
read -r -p "Please provide the full path to directory to save JSON file: " json_path
if [[ ! -w "${json_path}" ]]; then
echo "Invalid path or permission denied to write to given area."
json_path=""
fi
done
jsonOutput "${json_path}"; showMenu;;
8) branchTree; showMenu;;
9) branchesByDate; showMenu;;
10) contributors; showMenu;;
11) newDate=""
while [[ -z "${newDate}" ]]; do
read -r -p "Since what date? (e.g. '2023-04-13', '13 April 2023', 'last Thursday') " newDate
# Test if the date provide is valid and try again if it isn't.
if ! date -d "${newDate}" +%s > /dev/null 2>&1; then
newDate=""
fi
done
newContributors "${newDate}"; showMenu;;
12) commitsPerAuthor; showMenu;;
13) commitsPerDay; showMenu;;
14) commitsByMonth; showMenu;;
15) commitsByYear; showMenu;;
16) commitsByWeekday; showMenu;;
17) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByWeekday "${author}"; showMenu;;
18) commitsByHour; showMenu;;
19) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByHour "${author}"; showMenu;;
20) commitsByTimezone; showMenu;;
21) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByTimezone "${author}"; showMenu;;
22) suggestReviewers; showMenu;;
q|"\n") exit;;
*) clear; optionPicked "Pick an option from the menu"; showMenu;;
esac
done
showMenu
while [[ "${opt}" != "" ]]; do
clear
case "${opt}" in
1) detailedGitStats; showMenu;;
2) branch=""
while [[ -z "${branch}" ]]; do
read -r -p "Which branch? " branch
done
detailedGitStats "${branch}"; showMenu;;
3) changelogs; showMenu;;
4) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
changelogs "${author}"; showMenu;;
5) myDailyStats; showMenu;;
6) branch=""
while [[ -z "${branch}" ]]; do
read -r -p "Which branch? " branch
done
csvOutput "${branch}"; showMenu;;
7) json_path=""
while [[ -z "${json_path}" ]]; do
echo "NOTE: This feature is in beta!"
echo "The file name will be saved as \"output.json\"."
echo "The full path must be provided."
echo "Variables, subshell commands, or shorthands such as ~ may not be valid."
echo "You do not need the final slash at the end of a directory path."
echo "You must have write permission to the folder you are trying to save this to."
echo "This feature only works interactively and cannot be combined with other options."
echo -e "Example of a valid path: /home/$(whoami)\n"
read -r -p "Please provide the full path to directory to save JSON file: " json_path
if [[ ! -w "${json_path}" ]]; then
echo "Invalid path or permission denied to write to given area."
json_path=""
fi
done
jsonOutput "${json_path}"; showMenu;;
8) branchTree; showMenu;;
9) branchesByDate; showMenu;;
10) contributors; showMenu;;
11) newDate=""
while [[ -z "${newDate}" ]]; do
read -r -p "Since what date? (e.g. '2023-04-13', '13 April 2023', 'last Thursday') " newDate
if ! date -d "${newDate}" +%s > /dev/null 2>&1; then
newDate=""
fi
done
newContributors "${newDate}"; showMenu;;
12) commitsPerAuthor; showMenu;;
13) commitsPerDay; showMenu;;
14) commitsByMonth; showMenu;;
15) commitsByYear; showMenu;;
16) commitsByWeekday; showMenu;;
17) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByWeekday "${author}"; showMenu;;
18) commitsByHour; showMenu;;
19) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByHour "${author}"; showMenu;;
20) commitsByTimezone; showMenu;;
21) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsByTimezone "${author}"; showMenu;;
22) suggestReviewers; showMenu;;
23) author=""
while [[ -z "${author}" ]]; do
read -r -p "Which author? " author
done
commitsCalendarByAuthor "${author}"; showMenu;;
q|"\n") exit;;
*) clear; optionPicked "Pick an option from the menu"; showMenu;;
esac
done
fi

View File

@@ -1,4 +1,4 @@
.TH git-quick-stats "1" "April 2024" "git-quick-stats" "User Commands"
.TH git-quick-stats "1" "June 2025" "git-quick-stats" "User Commands"
.SH NAME
.B git\-quick\-stats
\- Simple and efficient way to access various stats in a git repository.
@@ -124,6 +124,11 @@ displays a list of commits per timezone
.IP
displays a list of commits per timezone by author
.HP
.PP
\fB\-\-activity\-calendar\fR
.IP
displays a calendar-style grid of commit activity per day-of-week and month for a selected author.
.HP
.SH SUGGEST OPTIONS
.PP
\fB\-r\fR, \fB\-\-suggest\-reviewers\fR
@@ -160,16 +165,49 @@ You can also set _GIT_MERGE_VIEW to only show merge commits, example:
.PP
.B export _GIT_MERGE_VIEW="exclusive"
.PP
You can switch to the legacy color scheme, 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
.B export _MENU_THEME=legacy
.PP
or
.B export _MENU_THEME=none
.PP
You can set _GIT_BRANCH to set the branch of the stats, example:
.PP
.B export _GIT_BRANCH="master"
.PP
.SH Calendar activity output
.PP
\fBactivity-calendar\fR outputs a visual grid of commit activity for a selected author, grouped by day-of-week (rows: Mon..Sun) and month (columns: Jan..Dec). Each cell is 3 characters wide, separated by one space.
.PP
Sample output:
.PP
.nf
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Mon ▓▓▓ ░░░ ▒▒▒ ░░░ ░░░ ▒▒▒ ▓▓▓ ░░░ ░░░ ▓▓▓ ▒▒▒ ▒▒▒
Tue ▒▒▒ ░░░ ▒▒▒ ░░░ ▒▒▒ ░░░ ▒▒▒ ▓▓▓ ▒▒▒ ░░░ ░░░ ░░░
Wed ░░░ ▓▓▓ ░░░ ▓▓▓ ▒▒▒ ░░░ ░░░ ▒▒▒ ░░░ ░░░ ▓▓▓ ░░░
Thu ░░░ ▒▒▒ ░░░ ░░░ ▒▒▒ ░░░ ▓▓▓ ▒▒▒ ▒▒▒ ░░░ ░░░ ▒▒▒
Fri ▒▒▒ ░░░ ▒▒▒ ▓▓▓ ░░░ ▓▓▓ ▒▒▒ ░░░ ▒▒▒ ░░░ ▒▒▒ ░░░
Sat ░░░ ░░░ ▒▒▒ ░░░ ░░░ ░░░ ▒▒▒ ░░░ ▒▒▒ ▓▓▓ ▒▒▒ ░░░
Sun ▓▓▓ ░░░ ▓▓▓ ░░░ ░░░ ▓▓▓ ░░░ ▒▒▒ ░░░ ░░░ ▓▓▓ ░░░
Legend: ... = 0 ░░░ = 12 ▒▒▒ = 35 ▓▓▓ = 6+ commits
.PP
You can set _GIT_IGNORE_AUTHORS to filter out specific authors, example:
.PP
.B export _GIT_IGNORE_AUTHORS="(author@examle.com|username)"
.
.fi
.PP
.SH SEE ALSO
.BR git (1)
.PP
Project homepage:
.UR https://github.com/git-quick-stats/git-quick-stats
.UE

View File

@@ -11,7 +11,7 @@
. tests/assert.sh -v
src="./git-quick-stats"
assert "$src fail" "Invalid argument
assert "$src fail" "Invalid argument: fail
NAME
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,
and files. Extracting this information is not always trivial, mostly because
of a gadzillion options to a gadzillion git commands.
This program allows you to see detailed information about a git repository.
GENERATE OPTIONS
@@ -92,17 +92,22 @@ ADDITIONAL USAGE
ex: export _GIT_MERGE_VIEW=enable
You can also set _GIT_MERGE_VIEW to only show merge commits
ex: export _GIT_MERGE_VIEW=exclusive
You can set _MENU_THEME to display the legacy color scheme
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\".
ex: export _MENU_THEME=legacy
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
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_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)"
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_contains "$src --commits-per-day" "Git commits per date"