Compare commits

...

46 Commits

Author SHA1 Message Date
Lukáš Mešťan
f5c2b82f02 Merge pull request #127 from pawaer/fix/issue126
Replaced grep commit by grep with regex including git sha hash
2021-04-29 08:36:34 +02:00
Pawaer
bf29c19cf5 Replaced grep commit by grep with regex including git sha hash
myDailyStats() used grep commit to detect count of commits. This
also increases the count by mentioning commit in the commit
message.

Therefore a grep with regex is used that expects a 40 digit/char
SHA1 hash after the word commit.
2021-04-24 21:50:30 +02:00
Lukáš Mešťan
c6cb74a5e0 Merge pull request #125 from arzzen/fix/issue-123
fix shortlog filter
2021-04-02 19:16:02 +02:00
arzzen
3f0befcd07 fix shortlog filter 2021-04-02 13:34:46 +02:00
Lukáš Mešťan
292876846d Merge pull request #124 from tomice/master
Fix escape sequences when outputting to non-TTYs
2021-03-31 17:31:28 +02:00
Tom Ice
e70c288225 Fix escape sequences when outputting to non-TTYs
* When performing actions such as redirecting the stats to a file,
  the terminal escape sequences for handling colors gets added
  to the top message in the text file. While running commands such
  as sed can fix this, it is inconvenient for users wishing to redirect
  or pipe the stats in a clean way.

  Attempting to use test -t yielded some unexpected results, as well
  as extra logic for handling a non-tty case, so for now, we are
  simply removing the colors from the message so it defaults to
  standard terminal colors and formatting

Fixes #122
2021-03-31 10:33:25 -04:00
Lukáš Mešťan
4d2621b084 Update LICENSE
bump year
2021-02-24 08:14:12 +01:00
Lukáš Mešťan
2318d42765 Merge pull request #121 from tomice/master
Change default date to be when git was first invented
2021-02-05 17:02:54 +01:00
Tom Ice
ef74d79102 Change default date to be when git was first invented
* The current default date is set to UNIX Epoch time. However, it seems
  some people are possibly having issues with date/time formats within
  their OSes. This commit attempts to fix issue #115 when the default
  date may be too old and cause no output to show
2021-01-29 19:31:29 -05:00
Lukáš Mešťan
82f8690285 Merge pull request #120 from vutny/fix-tests-assert-contains
test: fix assert_contains and its invocation
2020-12-19 19:55:30 +01:00
Denys Havrysh
0f641ea0e6 test: fix assert_contains and its invocation 2020-12-18 17:27:46 +02:00
Lukáš Mešťan
6c7bd4fddf Merge pull request #118 from vutny/fix-decimal-percent-view
fix: correct localized decimal percent numbers
2020-12-18 08:22:55 +01:00
Denys Havrysh
9f9617b88b fix: correct localized decimal percent numbers 2020-12-16 17:28:13 +02:00
Lukáš Mešťan
8cea653d6c Update FUNDING.yml 2020-09-09 12:52:08 +02:00
Lukáš Mešťan
efa004122e Update README.md 2020-08-25 18:07:35 +02:00
Lukáš Mešťan
0534327006 Merge pull request #114 from SuperSandro2000/patch-1
Mention apt install method
2020-08-14 10:19:35 +02:00
Sandro
a113dc306f Mention which releases have the package 2020-08-14 10:01:56 +02:00
Sandro
0fc2fe956b Mention apt install method 2020-08-12 22:51:14 +02:00
Lukáš Mešťan
1efe3133dc Merge pull request #113 from mcpcpc/master
usage: remove non-POSIX compliant features
2020-07-26 15:19:37 +02:00
Michael Czigler
3fd0ee2453 usage: remove non-POSIX compliant features 2020-07-23 09:33:37 -04:00
Lukáš Mešťan
a4f19c9e54 Merge pull request #112 from mcpcpc/usage
usage: remove non-POSIX compliant features
2020-07-23 13:48:48 +02:00
Michael Czigler
cd38c40fe8 usage: remove non-POSIX compliant features 2020-07-22 11:46:58 -04:00
Lukáš Mešťan
3283122a38 Merge pull request #109 from mcpcpc/master
checkUtils: remove non-POSIX compliant features
2020-07-22 17:18:59 +02:00
Michael Czigler
920b95c042 squash commit, remove non-POSIX features in checkUtils 2020-07-21 09:07:44 -04:00
Lukáš Mešťan
810ffcff54 update homebrew badge url 2020-07-14 19:05:50 +02:00
Lukáš Mešťan
f9df7ee5e2 Merge pull request #106 from arzzen/task/log-options
Ability to use options for git log command
2020-07-10 18:18:58 +02:00
arzzen
e31fc931c8 add git log options to myDailyStats 2020-07-10 18:13:01 +02:00
arzzen
0655814800 fix test 2020-07-10 08:02:28 +02:00
arzzen
1eb8c53f09 ability to use options for git log command 2020-07-09 09:21:25 +02:00
Lukáš Mešťan
3b2f26e85d Merge pull request #103 from tomice/master
Fix folder excluding pathspec bug #102
2020-06-28 12:59:29 +02:00
Tom Ice
93aa829eae Fix folder excluding pathspec bug #102
* There was an issue with quoting in the pathspec variable where
  the variable would get mangled when being passed to different
  parameters during git operations. This change fixes that.

Fixes #102
2020-06-27 20:02:50 -04:00
Lukáš Mešťan
1a11540b67 Merge pull request #101 from tomice/master
Fix divide by zero error during lines changed calc
2020-05-11 16:15:30 +02:00
Tom Ice
6692ec8aa3 Hotfix for non-GNU awk on macOS 2020-05-10 15:10:30 -04:00
Tom Ice
63eba9f1b8 Fix divide by zero error during lines changed calc
* A new feature was implemented to show percentages when displaying
  detailed stats (menu options 1 and 2, or options -T and -R).
  However, the calculation during "lines changed" may cause an error
  within awk stating it cannot divide by zero as there is no check
  to see if the divisor is larger than zero.

  This commit attempts to fix that issue, albeit admittedly not in
  the most elegant way...

Fixes #100
2020-05-10 14:57:09 -04:00
Lukáš Mešťan
228e67f879 Merge pull request #99 from tomice/master
Fix issue with env vars not respecting spaces
2020-05-07 10:29:58 +02:00
Tom Ice
891e2277ad Add info on how to use experimental JSON feature
* The JSON output feature was not originally explained well and needed
  some additional information on how it worked. Extra info on how to use
  this feature, as well as reminding people that this is a beta feature,
  was added to help the users when trying out this option

Fixes #96
2020-05-01 17:57:19 -04:00
Tom Ice
cc87b3046f Fix issue with env vars not respecting spaces
* If a space existed within _GIT_SINCE, _GIT_UNTIL, or _GIT_PATHSPEC,
  the shell would split the variable at the first space it saw because
  the variables were not quoted.

  If you were to simply quote the variables, then a null variable would
  get inserted into git's log, and it would cause an error.

  This change adds default values to these variables at all times so
  the variables are always assigned to something that git understands.

Fixes #95
2020-05-01 15:29:33 -04:00
Lukáš Mešťan
7aea224e21 Merge pull request #98 from novadiscovery/97-new-lines-as-percent
add percentage for new lines as well
2020-05-01 11:37:49 +02:00
frederic.cogny
c37492bb4c chore(git-quick-stats): add tab instead of space for nicer alignment between percentage counts 2020-05-01 01:03:21 +02:00
frederic.cogny
c258e34afe feat(git-quick-stats): add percentage for new lines as well 2020-05-01 00:36:10 +02:00
Lukáš Mešťan
9e18cf35d4 Merge pull request #94 from arzzen/patch-issue-91
Update commitsByWeekday sorting
2020-02-18 11:31:35 +01:00
Lukáš Mešťan
24ae67ae57 update commitsByWeekday sorting
Fix inconsistent sum sorting in by-weekday, #91
2020-02-14 09:34:46 +01:00
Lukáš Mešťan
ed0e3cbb6b Merge pull request #93 from tomice/master
Fixing OS X compatibility with merge feature
2020-02-13 09:42:06 +01:00
Tom Ice
46a771138e Fixing OS X compatibility with merge feature
* OS X utilizes an older version of GNU Bash. As such, certain features
  such as lowercase expansion can fail. This commit removes the Bash 4.0
  syntax in favor of a POSIX syntax with awk.
2020-02-11 08:25:44 -05:00
Lukáš Mešťan
9f54b87ed5 Update FUNDING.yml 2020-02-05 09:33:07 +00:00
Lukáš Mešťan
246076f5f6 Bump year 2020-01-20 08:00:06 +00:00
7 changed files with 289 additions and 135 deletions

2
.github/FUNDING.yml vendored
View File

@@ -2,3 +2,5 @@
github: [arzzen] github: [arzzen]
open_collective: git-quick-stats open_collective: git-quick-stats
ko_fi: lukasmestan
custom: ['https://lukasmestan.com/thanks/']

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 Lukáš Mešťan Copyright (c) 2021 Lukáš Mešťan
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,5 +1,5 @@
# GIT quick statistics [![Backers on Open Collective](https://opencollective.com/git-quick-stats/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/git-quick-stats/sponsors/badge.svg)](#sponsors) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Simple%20and%20efficient%20way%20to%20access%20various%20statistics%20in%20git%20repository&url=https://github.com/arzzen/git-quick-stat&via=arzzen&hashtags=git,stats,tool,statistics,developers) [![Travis](https://api.travis-ci.org/arzzen/git-quick-stats.svg?branch=master)](https://travis-ci.org/arzzen/git-quick-stats) [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/git-quick-stats.svg)](http://braumeister.org/formula/git-quick-stats) [![Linuxbrew package](https://repology.org/badge/version-for-repo/linuxbrew/git-quick-stats.svg)](https://repology.org/metapackage/git-quick-stats/packages) # GIT quick statistics [![Backers on Open Collective](https://opencollective.com/git-quick-stats/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/git-quick-stats/sponsors/badge.svg)](#sponsors) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Simple%20and%20efficient%20way%20to%20access%20various%20statistics%20in%20git%20repository&url=https://github.com/arzzen/git-quick-stat&via=arzzen&hashtags=git,stats,tool,statistics,developers) [![Travis](https://api.travis-ci.org/arzzen/git-quick-stats.svg?branch=master)](https://travis-ci.org/arzzen/git-quick-stats) [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/git-quick-stats.svg)](https://formulae.brew.sh/formula/git-quick-stats#default) [![Linuxbrew package](https://repology.org/badge/version-for-repo/linuxbrew/git-quick-stats.svg)](https://repology.org/metapackage/git-quick-stats/packages)
> `git-quick-stats` is a simple and efficient way to access various statistics in a git repository. > `git-quick-stats` is a simple and efficient way to access various statistics in a git repository.
> >
@@ -18,6 +18,7 @@
* [**Command-line arguments**](#command-line-arguments) * [**Command-line arguments**](#command-line-arguments)
* [**Git log since and until**](#git-log-since-and-until) * [**Git log since and until**](#git-log-since-and-until)
* [**Git log limit**](#git-log-limit) * [**Git log limit**](#git-log-limit)
* [**Git log options**](#git-log-options)
* [**Git pathspec**](#git-pathspec) * [**Git pathspec**](#git-pathspec)
* [**Git merge view strategy**](#git-merge-view-strategy) * [**Git merge view strategy**](#git-merge-view-strategy)
* [**Color themes**](#color-themes) * [**Color themes**](#color-themes)
@@ -146,6 +147,14 @@ You can set variable `_GIT_LIMIT` for limited output. It will affect the "change
export _GIT_LIMIT=20 export _GIT_LIMIT=20
``` ```
### Git log options
You can set _GIT_LOG_OPTIONS for [git log options](https://git-scm.com/docs/git-log#_options):
```bash
export _GIT_LOG_OPTIONS="--ignore-all-space --ignore-blank-lines"
```
### Git pathspec ### Git pathspec
You can exclude a directory from the stats by using [pathspec](https://git-scm.com/docs/gitglossary#gitglossary-aiddefpathspecapathspec) You can exclude a directory from the stats by using [pathspec](https://git-scm.com/docs/gitglossary#gitglossary-aiddefpathspecapathspec)
@@ -181,6 +190,14 @@ export _MENU_THEME=legacy
## Installation ## Installation
### Debian and Ubuntu
If you are on at least Debian Bullseye or Ubuntu Focal you can use apt for installation:
```bash
apt install git-quick-stats
```
### UNIX and Linux ### UNIX and Linux
```bash ```bash
@@ -240,6 +257,11 @@ You can use the Docker image provided:
*A:* You can run the dos2unix app in cygwin as follows: `/bin/dos2unix.exe /usr/local/bin/git-quick-stats`. This will convert the script from the CR-LF convention that Microsoft uses to the LF convention that UNIX, OS X, and Linux use. You should then should be able to run it as normal. *A:* You can run the dos2unix app in cygwin as follows: `/bin/dos2unix.exe /usr/local/bin/git-quick-stats`. This will convert the script from the CR-LF convention that Microsoft uses to the LF convention that UNIX, OS X, and Linux use. You should then should be able to run it as normal.
*Q:* How they could be used in a project with many git projects and statistics would show a summary of all git projects?
*A:* If you want to include submodule logs, you can try using the following: `export _GIT_LOG_OPTIONS="-p --submodule=log"`
(more info about [git log --submodule](https://git-scm.com/docs/git-log#Documentation/git-log.txt---submoduleltformatgt))
## Contribution ## Contribution
Want to contribute? Great! First, read this page. Want to contribute? Great! First, read this page.

View File

@@ -8,24 +8,42 @@ set -o nounset
set -o errexit set -o errexit
# Beginning git log date. Respects all git datetime formats # Beginning git log date. Respects all git datetime formats
# If $_GIT_SINCE is never set, choose epoch time as that is
# as far back as git will allow you to go
_since=${_GIT_SINCE:-} _since=${_GIT_SINCE:-}
[[ -n "${_since}" ]] && _since="--since=$_since" if [[ -n "${_since}" ]]; then
_since="--since=$_since"
else
_since="--since=2005-04-07"
fi
# End of git log date. Respects all git datetime formats # End of git log date. Respects all git datetime formats
# If $_GIT_UNTIL is never set, choose the latest system
# time from the user's current environment
_until=${_GIT_UNTIL:-} _until=${_GIT_UNTIL:-}
[[ -n "${_until}" ]] && _until="--until=$_until" if [[ -n "${_until}" ]]; then
_until="--until=$_until"
else
_until="--until=$(date)"
fi
# Set files or directories to be excluded in stats # Set files or directories to be excluded in stats
# If $_GIT_PATHSPEC is not set, shift over the option completely
_pathspec=${_GIT_PATHSPEC:-} _pathspec=${_GIT_PATHSPEC:-}
[[ -n "${_pathspec}" ]] && _pathspec="-- $_pathspec" if [[ -n "${_pathspec}" ]]; then
_pathspec="-- $_pathspec"
else
_pathspec="--"
fi
# Set merge commit view strategy. Default is to show no merge commits # Set merge commit view strategy. Default is to show no merge commits
# Exclusive shows only merge commits # Exclusive shows only merge commits
# Enable shows regular commits together with normal commits # Enable shows regular commits together with normal commits
_merges=${_GIT_MERGE_VIEW:-} _merges=${_GIT_MERGE_VIEW:-}
if [[ "${_merges,,}" == "exclusive" ]]; then _merges=$(echo "$_merges" | awk '{print tolower($0)}')
if [[ "${_merges}" == "exclusive" ]]; then
_merges="--merges" _merges="--merges"
elif [[ "${_merges,,}" == "enable" ]]; then elif [[ "${_merges}" == "enable" ]]; then
_merges="" _merges=""
else else
_merges="--no-merges" _merges="--no-merges"
@@ -39,6 +57,14 @@ else
_limit=10 _limit=10
fi fi
# Log options
_log_options=${_GIT_LOG_OPTIONS:-}
if [[ -n "${_log_options}" ]]; then
_log_options=$_log_options
else
_log_options=""
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}"
@@ -48,14 +74,14 @@ _theme="${_MENU_THEME:=default}"
# ARGS: None # ARGS: None
# OUTS: None # OUTS: None
################################################################################ ################################################################################
function checkUtils() { checkUtils() {
local -r msg="not found. Please make sure this is installed and in PATH." readonly MSG="not found. Please make sure this is installed and in PATH."
declare -ar utils=("awk" "basename" "cat" "column" "echo" "git" "grep" "head" readonly UTILS="awk basename cat column echo git grep head seq sort tput \
"seq" "sort" "tput" "tr" "uniq" "wc") tr uniq wc"
for u in "${utils[@]}" for u in $UTILS
do do
command -v "$u" >/dev/null 2>&1 || { echo >&2 "$u ${msg}"; exit 1; } command -v "$u" >/dev/null 2>&1 || { echo >&2 "$u ${MSG}"; exit 1; }
done done
} }
@@ -64,16 +90,16 @@ function checkUtils() {
# ARGS: None # ARGS: None
# OUTS: None # OUTS: None
################################################################################ ################################################################################
function usage() { usage() {
local -r program=$(basename "$0") readonly PROGRAM=$(basename "$0")
echo " echo "
NAME NAME
${program} - Simple and efficient way to access various stats in a git repo ${PROGRAM} - Simple and efficient way to access various stats in a git repo
SYNOPSIS SYNOPSIS
For non-interactive mode: ${program} [OPTIONS] For non-interactive mode: ${PROGRAM} [OPTIONS]
For interactive mode: ${program} For interactive mode: ${PROGRAM}
DESCRIPTION DESCRIPTION
Any git repository contains tons of information about commits, contributors, Any git repository contains tons of information about commits, contributors,
@@ -123,6 +149,8 @@ ADDITIONAL USAGE
ex: export _GIT_SINCE=\"2017-01-20\" ex: export _GIT_SINCE=\"2017-01-20\"
You can set _GIT_LIMIT for limited output log You can set _GIT_LIMIT for limited output log
ex: export _GIT_LIMIT=20 ex: export _GIT_LIMIT=20
You can set _GIT_LOG_OPTIONS for git log options
ex: export _GIT_LOG_OPTIONS=\"--ignore-all-space --ignore-blank-lines\"
You can exclude directories or files from the stats by using pathspec You can exclude directories or files from the stats by using pathspec
ex: export _GIT_PATHSPEC=':!pattern' ex: export _GIT_PATHSPEC=':!pattern'
You can set _GIT_MERGE_VIEW to view merge commits with normal commits You can set _GIT_MERGE_VIEW to view merge commits with normal commits
@@ -138,55 +166,55 @@ ADDITIONAL USAGE
# ARGS: None # ARGS: None
# OUTS: $opt: Option selected by the user based on menu choice # OUTS: $opt: Option selected by the user based on menu choice
################################################################################ ################################################################################
function showMenu() { showMenu() {
local -r normal=$(tput sgr0) NORMAL=$(tput sgr0)
local -r cyan=$(tput setaf 6) CYAN=$(tput setaf 6)
local -r bold=$(tput bold) BOLD=$(tput bold)
local -r red=$(tput setaf 1) RED=$(tput setaf 1)
local -r yellow=$(tput setaf 3) YELLOW=$(tput setaf 3)
local -r white=$(tput setaf 7) WHITE=$(tput setaf 7)
local titles="" TITLES=""
local text="" TEXT=""
local nums="" NUMS=""
local help_txt="" HELP_TXT=""
local exit_txt="" EXIT_TXT=""
# Adjustable color menu option # Adjustable color menu option
if [[ "${_theme}" == "legacy" ]]; then if [[ "${_theme}" == "legacy" ]]; then
titles="${bold}${red}" && readonly titles TITLES="${BOLD}${RED}"
text="${normal}${cyan}" && readonly text TEXT="${NORMAL}${CYAN}"
nums="${bold}${yellow}" && readonly nums NUMS="${BOLD}${YELLOW}"
help_txt="${normal}${yellow}" && readonly help_txt HELP_TXT="${NORMAL}${YELLOW}"
exit_txt="${bold}${red}" && readonly exit_txt EXIT_TXT="${BOLD}${RED}"
else else
titles="${bold}${cyan}" && readonly titles TITLES="${BOLD}${CYAN}"
text="${normal}${white}" && readonly text TEXT="${NORMAL}${WHITE}"
nums="${normal}${bold}${white}" && readonly nums NUMS="${NORMAL}${BOLD}${WHITE}"
help_txt="${normal}${cyan}" && readonly help_txt HELP_TXT="${NORMAL}${CYAN}"
exit_txt="${bold}${cyan}" && readonly exit_txt EXIT_TXT="${BOLD}${CYAN}"
fi fi
echo -e "\n${titles} Generate:${normal}" printf %b "\\n${TITLES} Generate:${NORMAL}\\n"
echo -e "${nums} 1)${text} Contribution stats (by author)" printf %b "${NUMS} 1)${TEXT} Contribution stats (by author)\\n"
echo -e "${nums} 2)${text} Contribution stats (by author) on a specific branch" printf %b "${NUMS} 2)${TEXT} Contribution stats (by author) on a specific branch\\n"
echo -e "${nums} 3)${text} Git changelogs (last $_limit days)" printf %b "${NUMS} 3)${TEXT} Git changelogs (last $_limit days)\\n"
echo -e "${nums} 4)${text} Git changelogs by author" printf %b "${NUMS} 4)${TEXT} Git changelogs by author\\n"
echo -e "${nums} 5)${text} My daily status" printf %b "${NUMS} 5)${TEXT} My daily status\\n"
echo -e "${nums} 6)${text} Save git log output in JSON format" printf %b "${NUMS} 6)${TEXT} Save git log output in JSON format\\n"
echo -e "\n${titles} List:" printf %b "\\n${TITLES} List:\\n"
echo -e "${nums} 7)${text} Branch tree view (last $_limit)" printf %b "${NUMS} 7)${TEXT} Branch tree view (last $_limit)\\n"
echo -e "${nums} 8)${text} All branches (sorted by most recent commit)" printf %b "${NUMS} 8)${TEXT} All branches (sorted by most recent commit)\\n"
echo -e "${nums} 9)${text} All contributors (sorted by name)" printf %b "${NUMS} 9)${TEXT} All contributors (sorted by name)\\n"
echo -e "${nums} 10)${text} Git commits per author" printf %b "${NUMS} 10)${TEXT} Git commits per author\\n"
echo -e "${nums} 11)${text} Git commits per date" printf %b "${NUMS} 11)${TEXT} Git commits per date\\n"
echo -e "${nums} 12)${text} Git commits per month" printf %b "${NUMS} 12)${TEXT} Git commits per month\\n"
echo -e "${nums} 13)${text} Git commits per weekday" printf %b "${NUMS} 13)${TEXT} Git commits per weekday\\n"
echo -e "${nums} 14)${text} Git commits per hour" printf %b "${NUMS} 14)${TEXT} Git commits per hour\\n"
echo -e "${nums} 15)${text} Git commits by author per hour" printf %b "${NUMS} 15)${TEXT} Git commits by author per hour\\n"
echo -e "\n${titles} Suggest:" printf %b "\\n${TITLES} Suggest:\\n"
echo -e "${nums} 16)${text} Code reviewers (based on git history)" printf %b "${NUMS} 16)${TEXT} Code reviewers (based on git history)\\n"
echo -e "\n${help_txt}Please enter a menu option or ${exit_txt}press Enter to exit." printf %b "\\n${HELP_TXT}Please enter a menu option or ${EXIT_TXT}press Enter to exit.\\n"
echo -n "${text}> ${normal}" printf %b "${TEXT}> ${NORMAL}"
read -r opt read -r opt
} }
@@ -196,12 +224,9 @@ function showMenu() {
# OUTS: None # OUTS: None
################################################################################ ################################################################################
function optionPicked() { function optionPicked() {
local -r bold=$(tput bold) local msg=${*:-"Error: No message passed"}
local -r red=$(tput setaf 1)
local -r reset=$(tput sgr0)
local msg=${*:-"${reset}Error: No message passed"}
echo -e "${bold}${red}${msg}${reset}\n" echo -e "${msg}\n"
} }
################################################################################ ################################################################################
@@ -239,32 +264,39 @@ function detailedGitStats() {
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 $_pathspec | LC_ALL=C awk ' "$_since" "$_until" $_log_options $_pathspec | LC_ALL=C awk '
function printStats(author) { function printStats(author) {
printf "\t%s:\n", author printf "\t%s:\n", author
if(more["total"] > 0) { if(more["total"] > 0) {
printf "\t insertions: %d (%.0f%%)\n", more[author], \ printf "\t insertions: %d\t(%.0f%%)\n", more[author], \
(more[author] / more["total"] * 100) (more[author] / more["total"] * 100)
} }
if(less["total"] > 0) { if(less["total"] > 0) {
printf "\t deletions: %d (%.0f%%)\n", less[author], \ printf "\t deletions: %d\t(%.0f%%)\n", less[author], \
(less[author] / less["total"] * 100) (less[author] / less["total"] * 100)
} }
if(file["total"] > 0) { if(file["total"] > 0) {
printf "\t files: %d (%.0f%%)\n", file[author], \ printf "\t files: %d\t(%.0f%%)\n", file[author], \
(file[author] / file["total"] * 100) (file[author] / file["total"] * 100)
} }
if(commits["total"] > 0) { if(commits["total"] > 0) {
printf "\t commits: %d (%.0f%%)\n", commits[author], \ printf "\t commits: %d\t(%.0f%%)\n", commits[author], \
(commits[author] / commits["total"] * 100) (commits[author] / commits["total"] * 100)
} }
if (first[author] != "") { if (first[author] != "") {
printf "\t lines changed: %s\n", more[author] + less[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 first commit: %s\n", first[author]
printf "\t last commit: %s\n", last[author] printf "\t last commit: %s\n", last[author]
} }
@@ -288,8 +320,8 @@ function detailedGitStats() {
/^[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 more["total"] += $1
less["total"] += $2 less["total"] += $2
file["total"] += 1 file["total"] += 1
@@ -312,8 +344,8 @@ function detailedGitStats() {
################################################################################ ################################################################################
function suggestReviewers() { function suggestReviewers() {
optionPicked "Suggested code reviewers (based on git history):" optionPicked "Suggested code reviewers (based on git history):"
git -c log.showSignature=false log --use-mailmap $_merges $_since $_until \ git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \
--pretty=%aN $_pathspec | head -n 100 | sort | uniq -c | sort -nr | LC_ALL=C awk ' --pretty=%aN $_log_options $_pathspec | head -n 100 | sort | uniq -c | sort -nr | LC_ALL=C awk '
{ args[NR] = $0; } { args[NR] = $0; }
END { END {
for (i = 1; i <= NR; ++i) { for (i = 1; i <= NR; ++i) {
@@ -328,13 +360,13 @@ function suggestReviewers() {
# OUTS: A JSON formatted file # OUTS: A JSON formatted file
################################################################################ ################################################################################
function jsonOutput() { function jsonOutput() {
optionPicked "Output log saved to file at: ${json_path:?}/output.json" optionPicked "Output log saved to file at: ${json_path}/output.json"
git -c log.showSignature=false log --use-mailmap $_merges $_since $_until \ git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" $_log_options \
--pretty=format:'{%n "commit": "%H",%n "abbreviated_commit": "%h",%n "tree": "%T",%n "abbreviated_tree": "%t",%n "parent": "%P",%n "abbreviated_parent": "%p",%n "refs": "%D",%n "encoding": "%e",%n "subject": "%s",%n "sanitized_subject_line": "%f",%n "body": "%b",%n "commit_notes": "%N",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "date": "%aD"%n },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "date": "%cD"%n }%n},' \ --pretty=format:'{%n "commit": "%H",%n "abbreviated_commit": "%h",%n "tree": "%T",%n "abbreviated_tree": "%t",%n "parent": "%P",%n "abbreviated_parent": "%p",%n "refs": "%D",%n "encoding": "%e",%n "subject": "%s",%n "sanitized_subject_line": "%f",%n "body": "%b",%n "commit_notes": "%N",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "date": "%aD"%n },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "date": "%cD"%n }%n},' \
| sed "$ s/,$//" \ | sed "$ s/,$//" \
| sed ':a;N;$!ba;s/\r\n\([^{]\)/\\n\1/g' \ | sed ':a;N;$!ba;s/\r\n\([^{]\)/\\n\1/g' \
| awk 'BEGIN { print("[") } { print($0) } END { print("]") }' \ | awk 'BEGIN { print("[") } { print($0) } END { print("]") }' \
> "${json_path:?}"/output.json > "${json_path}/output.json"
} }
################################################################################ ################################################################################
@@ -345,11 +377,13 @@ function jsonOutput() {
function commitsByMonth() { function commitsByMonth() {
optionPicked "Git commits by month:" optionPicked "Git commits by month:"
echo -e "\tmonth\tsum" echo -e "\tmonth\tsum"
local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}")
local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}")
for i in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec for i in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
do do
echo -en "\t$i\t" echo -en "\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' \
$_since $_until | grep " $i " | wc -l "$_since" "$_until" $_log_options | grep -E "($startYear|$endYear)" | grep " $i " | wc -l
done | awk '{ done | awk '{
count[$1] = $2 count[$1] = $2
total += $2 total += $2
@@ -376,21 +410,23 @@ function commitsByMonth() {
function commitsByWeekday() { function commitsByWeekday() {
optionPicked "Git commits by weekday:" optionPicked "Git commits by weekday:"
echo -e "\tday\tsum" echo -e "\tday\tsum"
local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}")
local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}")
local counter=1
for i in Mon Tue Wed Thu Fri Sat Sun for i in Mon Tue Wed Thu Fri Sat Sun
do do
echo -en "\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' \
$_since $_until | grep "$i " | wc -l "$_since" "$_until" $_log_options | grep -E "($startYear|$endYear)" | grep "$i " | wc -l
counter=$((counter+1))
done | awk '{ done | awk '{
} }
NR == FNR { NR == FNR {
count[$1] = $2; count[$1" "$2] = $3;
total += $2; total += $3;
next next
} }
END{ END{
for (day in count) { for (day in count) {
s="|"; s="|";
if (total > 0) { if (total > 0) {
@@ -398,10 +434,10 @@ function commitsByWeekday() {
for (i = 1; i <= percent; ++i) { for (i = 1; i <= percent; ++i) {
s=s"█" s=s"█"
} }
printf( "\t%s\t%-0s\t%s\n", day, 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 2 -n -r }' | sort -k 1 -n | awk '{$1=""}1' | awk '{$1=$1}1' | awk '{printf("\t%s\t%s\t%s\n", $1, $2, $3)}'
} }
################################################################################ ################################################################################
@@ -421,11 +457,14 @@ function commitsByHour() {
_author="--author=${author}" _author="--author=${author}"
fi fi
echo -e "\thour\tsum" echo -e "\thour\tsum"
local startYear=$(echo "$_since" | grep -Eo "[0-9]{4}")
local endYear=$(echo "$_until" | grep -Eo "[0-9]{4}")
for i in $(seq -w 0 23) for i in $(seq -w 0 23)
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 | grep ' '$i: | wc -l "${_author}" "$_since" "$_until" $_log_options | grep -E "($startYear|$endYear)" | grep ' '$i: | wc -l
done | awk '{ done | awk '{
count[$1] = $2 count[$1] = $2
total += $2 total += $2
@@ -452,8 +491,8 @@ function commitsByHour() {
################################################################################ ################################################################################
function commitsPerDay() { function commitsPerDay() {
optionPicked "Git commits per date:"; optionPicked "Git commits per date:";
git -c log.showSignature=false log --use-mailmap $_merges $_since $_until \ git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \
--date=short --format='%ad' $_pathspec | sort | uniq -c --date=short --format='%ad' $_log_options $_pathspec | sort | uniq -c
} }
################################################################################ ################################################################################
@@ -465,9 +504,9 @@ function commitsPerDay() {
function commitsPerAuthor() { function commitsPerAuthor() {
optionPicked "Git commits per author:" optionPicked "Git commits per author:"
local authorCommits=$(git -c log.showSignature=false log --use-mailmap $_merges \ local authorCommits=$(git -c log.showSignature=false log --use-mailmap $_merges \
$_since $_until | grep -i Author: | cut -c9-) "$_since" "$_until" $_log_options | grep -i Author: | cut -c9-)
local coAuthorCommits=$(git -c log.showSignature=false log --use-mailmap $_merges \ local coAuthorCommits=$(git -c log.showSignature=false log --use-mailmap $_merges \
$_since $_until | grep -i Co-Authored-by: | cut -c21-) "$_since" "$_until" $_log_options | grep -i Co-Authored-by: | cut -c21-)
if [[ -z "${coAuthorCommits}" ]] if [[ -z "${coAuthorCommits}" ]]
then then
@@ -483,9 +522,9 @@ function commitsPerAuthor() {
sum[author[i]]++; name[author[i]] = author[i]; total++; sum[author[i]]++; name[author[i]] = author[i]; total++;
} }
for(i in sum) { for(i in sum) {
printf "\t%d,%s,%2.1f%%\n", sum[i], name[i], (100 * sum[i] / total) printf "\t%d:%s:%2.1f%%\n", sum[i], name[i], (100 * sum[i] / total)
} }
}' | sort -n -r | column -t -s, }' | sort -n -r | column -t -s:
} }
################################################################################ ################################################################################
@@ -506,8 +545,8 @@ function myDailyStats() {
echo -e "\t" $(git -c log.showSignature=false log --use-mailmap \ echo -e "\t" $(git -c log.showSignature=false log --use-mailmap \
--author="$(git config user.name)" $_merges \ --author="$(git config user.name)" $_merges \
--since=$(date "+%Y-%m-%dT00:00:00") \ --since=$(date "+%Y-%m-%dT00:00:00") \
--until=$(date "+%Y-%m-%dT23:59:59") --reverse \ --until=$(date "+%Y-%m-%dT23:59:59") --reverse $_log_options \
| grep commit | wc -l) "commits" | grep -E "commit [a-f0-9]{40}" | wc -l) "commits"
} }
################################################################################ ################################################################################
@@ -517,8 +556,8 @@ function myDailyStats() {
################################################################################ ################################################################################
function contributors() { function contributors() {
optionPicked "All contributors (sorted by name):" optionPicked "All contributors (sorted by name):"
git -c log.showSignature=false log --use-mailmap $_merges $_since $_until \ git -c log.showSignature=false log --use-mailmap $_merges "$_since" "$_until" \
--format='%aN' $_pathspec | sort -u | cat -n --format='%aN' $_log_options $_pathspec | sort -u | cat -n
} }
################################################################################ ################################################################################
@@ -529,9 +568,9 @@ function contributors() {
function branchTree() { function branchTree() {
optionPicked "Branching tree view:" optionPicked "Branching tree view:"
git -c log.showSignature=false log --use-mailmap --graph --abbrev-commit \ git -c log.showSignature=false log --use-mailmap --graph --abbrev-commit \
$_since $_until --decorate \ "$_since" "$_until" --decorate \
--format=format:'--+ Commit: %h %n | Date: %aD (%ar) %n'' | Message: %s %d %n'' + Author: %aN %n' \ --format=format:'--+ Commit: %h %n | Date: %aD (%ar) %n'' | Message: %s %d %n'' + Author: %aN %n' \
--all | head -n $((_limit*5)) --all $_log_options | head -n $((_limit*5))
} }
################################################################################ ################################################################################
@@ -567,7 +606,7 @@ function changelogs() {
--use-mailmap \ --use-mailmap \
$_merges \ $_merges \
--format="%cd" \ --format="%cd" \
--date=short "${_author}" $_since $_until $_pathspec \ --date=short "${_author}" "$_since" "$_until" $_log_options $_pathspec \
| sort -u -r | head -n $_limit \ | sort -u -r | head -n $_limit \
| while read DATE; do | while read DATE; do
echo -e "\n[$DATE]" echo -e "\n[$DATE]"
@@ -624,7 +663,15 @@ if [[ "$#" -eq 1 ]]; then
-j|--json-output) -j|--json-output)
json_path="" json_path=""
while [[ -z "${json_path}" ]]; do while [[ -z "${json_path}" ]]; do
read -r -p "Path to save JSON file: " json_path 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 or shorthands such as ~ are not 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 if [[ ! -w "${json_path}" ]]; then
echo "Invalid path or permission denied to write to given area." echo "Invalid path or permission denied to write to given area."
json_path="" json_path=""
@@ -660,7 +707,15 @@ while [[ "${opt}" != "" ]]; do
5) myDailyStats; showMenu;; 5) myDailyStats; showMenu;;
6) json_path="" 6) json_path=""
while [[ -z "${json_path}" ]]; do while [[ -z "${json_path}" ]]; do
read -r -p "Path to save JSON file: " json_path 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 if [[ ! -w "${json_path}" ]]; then
echo "Invalid path or permission denied to write to given area." echo "Invalid path or permission denied to write to given area."
json_path="" json_path=""

View File

@@ -112,6 +112,10 @@ You can set _GIT_LIMIT for limited output log, example:
.PP .PP
.B export _GIT_LIMIT=20 .B export _GIT_LIMIT=20
.PP .PP
You can set _GIT_LOG_OPTIONS for git log options, example:
.PP
.B export _GIT_LOG_OPTIONS="--ignore-all-space --ignore-blank-lines"
.PP
You can exclude directories or files from the stats by using pathspec, example: You can exclude directories or files from the stats by using pathspec, example:
.PP .PP
.B export _GIT_PATHSPEC=':!pattern' .B export _GIT_PATHSPEC=':!pattern'

View File

@@ -17,12 +17,16 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
set -o nounset
export DISCOVERONLY=${DISCOVERONLY:-} export DISCOVERONLY=${DISCOVERONLY:-}
export DEBUG=${DEBUG:-} export DEBUG=${DEBUG:-}
export STOP=${STOP:-} export STOP=${STOP:-}
export INVARIANT=${INVARIANT:-} export INVARIANT=${INVARIANT:-}
export CONTINUE=${CONTINUE:-} export CONTINUE=${CONTINUE:-}
GREP=${GREP:-grep}
args="$(getopt -n "$0" -l \ args="$(getopt -n "$0" -l \
verbose,help,stop,discover,invariant,continue vhxdic $*)" \ verbose,help,stop,discover,invariant,continue vhxdic $*)" \
|| exit -1 || exit -1
@@ -132,7 +136,7 @@ assert_raises() {
_assert_with_grep() { _assert_with_grep() {
local grep_modifier="$1" local grep_modifier="$1"
local output="$($2)" local output="$($2)"
local exitcode="$4" || 0 local exitcode=0
shift 2 shift 2
while [ $# != 0 ]; do while [ $# != 0 ]; do

View File

@@ -3,16 +3,83 @@
. tests/assert.sh -v . tests/assert.sh -v
src="./git-quick-stats" src="./git-quick-stats"
assert "$src fail" "Invalid argument\n\nNAME\n git-quick-stats - Simple and efficient way to access various stats in a git repo\n\nSYNOPSIS\n For non-interactive mode: git-quick-stats [OPTIONS]\n For interactive mode: git-quick-stats\n\nDESCRIPTION\n Any git repository contains tons of information about commits, contributors,\n and files. Extracting this information is not always trivial, mostly because\n of a gadzillion options to a gadzillion git commands.\n\n This program allows you to see detailed information about a git repository.\n\nOPTIONS\n -r, --suggest-reviewers\n show the best people to contact to review code\n -T, --detailed-git-stats\n give a detailed list of git stats\n -R, --git-stats-by-branch\n see detailed list of git stats by branch\n -d, --commits-per-day\n displays a list of commits per day\n -m, --commits-by-month\n displays a list of commits per month\n -w, --commits-by-weekday\n displays a list of commits per weekday\n -o, --commits-by-hour\n displays a list of commits per hour\n -A, --commits-by-author-by-hour\n displays a list of commits per hour by author\n -a, --commits-per-author\n displays a list of commits per author\n -S, --my-daily-stats\n see your current daily stats\n -C, --contributors\n see a list of everyone who contributed to the repo\n -b, --branch-tree\n show an ASCII graph of the git repo branch history\n -D, --branches-by-date\n show branches by date\n -c, --changelogs\n see changelogs\n -L, --changelogs-by-author\n see changelogs by author\n -j, --json-output\n save git log as a JSON formatted file to a specified area\n -h, -?, --help\n display this help text in the terminal\n\nADDITIONAL USAGE\n You can set _GIT_SINCE and _GIT_UNTIL to limit the git time log\n ex: export _GIT_SINCE=\"2017-01-20\"\n You can set _GIT_LIMIT for limited output log\n ex: export _GIT_LIMIT=20\n You can exclude directories or files from the stats by using pathspec\n ex: export _GIT_PATHSPEC=':!pattern'\n You can set _GIT_MERGE_VIEW to view merge commits with normal commits\n ex: export _GIT_MERGE_VIEW=enable\n You can also set _GIT_MERGE_VIEW to only show merge commits\n ex: export _GIT_MERGE_VIEW=exclusive\n You can set _MENU_THEME to display the legacy color scheme\n ex: export _MENU_THEME=legacy" assert "$src fail" "Invalid argument
NAME
git-quick-stats - Simple and efficient way to access various stats in a git repo
SYNOPSIS
For non-interactive mode: git-quick-stats [OPTIONS]
For interactive mode: git-quick-stats
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.
OPTIONS
-r, --suggest-reviewers
show the best people to contact to review code
-T, --detailed-git-stats
give a detailed list of git stats
-R, --git-stats-by-branch
see detailed list of git stats by branch
-d, --commits-per-day
displays a list of commits per day
-m, --commits-by-month
displays a list of commits per month
-w, --commits-by-weekday
displays a list of commits per weekday
-o, --commits-by-hour
displays a list of commits per hour
-A, --commits-by-author-by-hour
displays a list of commits per hour by author
-a, --commits-per-author
displays a list of commits per author
-S, --my-daily-stats
see your current daily stats
-C, --contributors
see a list of everyone who contributed to the repo
-b, --branch-tree
show an ASCII graph of the git repo branch history
-D, --branches-by-date
show branches by date
-c, --changelogs
see changelogs
-L, --changelogs-by-author
see changelogs by author
-j, --json-output
save git log as a JSON formatted file to a specified area
-h, -?, --help
display this help text in the terminal
ADDITIONAL USAGE
You can set _GIT_SINCE and _GIT_UNTIL to limit the git time log
ex: export _GIT_SINCE=\"2017-01-20\"
You can set _GIT_LIMIT for limited output log
ex: export _GIT_LIMIT=20
You can set _GIT_LOG_OPTIONS for git log options
ex: export _GIT_LOG_OPTIONS=\"--ignore-all-space --ignore-blank-lines\"
You can exclude directories or files from the stats by using pathspec
ex: export _GIT_PATHSPEC=':!pattern'
You can set _GIT_MERGE_VIEW to view merge commits with normal commits
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
ex: export _MENU_THEME=legacy"
assert_raises "$src fail" 1 assert_raises "$src fail" 1
assert_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)" 127 assert_contains "$src --suggest-reviewers" "Suggested code reviewers (based on git history)"
assert_raises "$src --suggest-reviewers" 0 assert_raises "$src --suggest-reviewers" 0
assert_contains "$src --detailed-git-stats" "Contribution stats" 127 assert_contains "$src --detailed-git-stats" "Contribution stats"
assert_raises "$src --detailed-git-stats" 0 assert_raises "$src --detailed-git-stats" 0
assert_contains "$src --commits-per-day" "Git commits per date" 127 assert_contains "$src --commits-per-day" "Git commits per date"
assert_raises "$src --commits-per-day" 0 assert_raises "$src --commits-per-day" 0
assert_end assert_end