Merge pull request #197 from git-quick-stats/fix/issues-196

GNU date requirements for macOS and FreeBSD
This commit is contained in:
arzzen 2026-04-06 10:33:02 +02:00 committed by GitHub
commit b424bb9c17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 29 deletions

View file

@ -280,15 +280,16 @@ sudo make reinstall
### macOS (homebrew) ### macOS (homebrew)
macOS requires GNU coreutils to be installed and for the non "g" aliased `git-quick-stats` requires GNU `date`. On macOS, install GNU coreutils:
versions to be exported to your path. The following is an example of how to
perform this if you are using Homebrew as your package manager.
```bash ```bash
brew install coreutils brew install coreutils
export PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH" export PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"
``` ```
The script auto-detects GNU date as either `date` or `gdate`, so exporting
`gnubin` is optional for this project.
From there, you can install via Homebrew as follows: From there, you can install via Homebrew as follows:
```bash ```bash
@ -297,10 +298,20 @@ brew install git-quick-stats
Or you can follow the UNIX and Linux instructions if you wish. Or you can follow the UNIX and Linux instructions if you wish.
If you would like to default to using the GNU coreutils (recommended), then you If you would like to default to using GNU coreutils system-wide, add
can add `export PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"` to `export PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"` to your
your applicable `~/.bash_profile`, `~/.zprofile`, or other relevant profile applicable `~/.bash_profile`, `~/.zprofile`, or other relevant profile based
based on the shell of your choice. on the shell of your choice.
### FreeBSD
`git-quick-stats` requires GNU `date`. On FreeBSD, install GNU coreutils:
```bash
pkg install coreutils
```
FreeBSD provides GNU date as `gdate`, and the script auto-detects it.
### Windows ### Windows
@ -322,6 +333,7 @@ You can use the Docker image provided:
## System requirements ## System requirements
- An OS with a Bash shell - An OS with a Bash shell
- GNU `date` available as either `date` or `gdate`
- Tools we use: - Tools we use:
```bash ```bash

View file

@ -28,6 +28,15 @@ COLOR_DARKEST_RED=$(tput setaf 52)
COLOR_GRAY=$(tput setaf 240) COLOR_GRAY=$(tput setaf 240)
COLOR_RESET=$(tput sgr0) COLOR_RESET=$(tput sgr0)
# Detect GNU date command (may be 'gdate' on FreeBSD/macOS with coreutils)
if date --version >/dev/null 2>&1; then
_DATE_CMD="date"
elif gdate --version >/dev/null 2>&1; then
_DATE_CMD="gdate"
else
_DATE_CMD="date"
fi
# Beginning git log date. Respects all git datetime formats # Beginning git log date. Respects all git datetime formats
# If $_GIT_SINCE is never set, look at the repository to find the first date. # If $_GIT_SINCE is never set, look at the repository to find the first date.
# NOTE: previously this put the date at the fixed GIT epoch (May 2005) # NOTE: previously this put the date at the fixed GIT epoch (May 2005)
@ -45,7 +54,7 @@ _until=${_GIT_UNTIL:-}
if [[ -n "${_until}" ]]; then if [[ -n "${_until}" ]]; then
_until="--until=$_until" _until="--until=$_until"
else else
_until="--until=$(LC_TIME=C date '+%a, %d %b %Y %H:%M:%S %Z')" _until="--until=$(LC_TIME=C $_DATE_CMD '+%a, %d %b %Y %H:%M:%S %Z')"
fi fi
# Set files or directories to be excluded in stats # Set files or directories to be excluded in stats
@ -128,13 +137,13 @@ function commitsCalendarByAuthor() {
# Gather commit counts # Gather commit counts
git -c log.showSignature=false log --use-mailmap $_merges \ git -c log.showSignature=false log --use-mailmap $_merges \
--date=iso --author="$author" "$_since" "$_until" $_log_options \ --date=iso --author="$author" "$_since" "$_until" $_log_options \
--pretty='%ad' $_pathspec | awk ' --pretty='%ad' $_pathspec | awk -v date_cmd="$_DATE_CMD" '
{ {
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_cmd " -d \"" a[1] "\" +%u";
cmd | getline weekday; cmd | getline weekday;
close(cmd); close(cmd);
# weekday: 1=Mon, ..., 7=Sun # weekday: 1=Mon, ..., 7=Sun
@ -204,14 +213,14 @@ function commitsHeatmap() {
local i local i
for i in $(seq $((_commit_days-1)) -1 0); do for i in $(seq $((_commit_days-1)) -1 0); do
local day=$(date -d "-$i days" +"%Y-%m-%d") local day=$($_DATE_CMD -d "-$i days" +"%Y-%m-%d")
if [[ $(date -d "$day" +%u) -gt 5 ]]; then if [[ $($_DATE_CMD -d "$day" +%u) -gt 5 ]]; then
echo -en "${COLOR_GRAY}" echo -en "${COLOR_GRAY}"
else else
echo -en "${COLOR_RESET}" echo -en "${COLOR_RESET}"
fi fi
local dayName=$(date -d "$day" +%a) local dayName=$($_DATE_CMD -d "$day" +%a)
printf "%s | %s |" "$dayName" "$day" printf "%s | %s |" "$dayName" "$day"
declare -a commits_per_hour declare -a commits_per_hour
@ -264,7 +273,7 @@ function commitsHeatmap() {
################################################################################ ################################################################################
function checkUtils() { function checkUtils() {
readonly 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."
readonly UTILS="awk basename cat column date echo git grep head printf seq \ readonly UTILS="awk basename cat column echo git grep head printf seq \
sort tput tr uniq" sort tput tr uniq"
for u in $UTILS for u in $UTILS
@ -274,16 +283,13 @@ function checkUtils() {
# NOTE: The --version flag is only available in GNU date which is required # NOTE: The --version flag is only available in GNU date which is required
# for how the current date/time strings are used in this shell script. # for how the current date/time strings are used in this shell script.
# To fully support the legacy BSD date found in a default install within # On FreeBSD and macOS the system date is BSD date; GNU date may be
# macOS and older distributions of Linux and Unix, a handful of helper # installed as 'gdate' via coreutils. _DATE_CMD is auto-detected above.
# functions can probably be created to handle every case of incompatibility if ! "$_DATE_CMD" --version >/dev/null 2>&1; then
# between the two. Until that's implemented, it is probably best to warn
# the user that this will not work rather than having it silently bomb out
# during runtime.
if ! date --version >/dev/null 2>&1; then
echo "ERROR: GNU date is required." echo "ERROR: GNU date is required."
echo "If you're on macOS, please install it using 'brew install coreutils'." echo "On macOS, install it with: brew install coreutils"
echo "Ensure that your PATH is configured to use GNU date as well." echo "On FreeBSD, install it with: pkg install coreutils (provides gdate)"
echo "Ensure that GNU date (or gdate) is in your PATH."
echo "See the README.md for further details." echo "See the README.md for further details."
exit 1 exit 1
fi fi
@ -702,7 +708,7 @@ function changelogs() {
local _author="" local _author=""
local commits="" local commits=""
local author="${1:-}" local author="${1:-}"
local next=$(date +%F) local next=$($_DATE_CMD +%F)
if [[ -z "${author}" ]]; then if [[ -z "${author}" ]]; then
optionPicked "Git changelogs:" optionPicked "Git changelogs:"
@ -1021,7 +1027,7 @@ function newContributors() {
local firstCommit=$(git -c log.showSignature=false log --author="$c" \ local firstCommit=$(git -c log.showSignature=false log --author="$c" \
--reverse --use-mailmap $_merges "$_since" "$_until" \ --reverse --use-mailmap $_merges "$_since" "$_until" \
--format='%at' $_log_options $_pathspec | filter_ignored_authors | head -n 1) --format='%at' $_log_options $_pathspec | filter_ignored_authors | head -n 1)
if [[ $firstCommit -ge $(date -d "$newDate" +%s) ]]; then if [[ $firstCommit -ge $($_DATE_CMD -d "$newDate" +%s) ]]; then
echo "$c" echo "$c"
fi fi
done done
@ -1086,7 +1092,7 @@ function parse_year() {
# Handle the raw UNIX timestamp format i.e. 1697375696 +0000 # Handle the raw UNIX timestamp format i.e. 1697375696 +0000
if [[ "$date_str" =~ ^[0-9]+(\ [+-][0-9]{4})?$ ]]; then if [[ "$date_str" =~ ^[0-9]+(\ [+-][0-9]{4})?$ ]]; then
timestamp=$(echo "$date_str" | awk '{print $1}') timestamp=$(echo "$date_str" | awk '{print $1}')
year=$(date -d "@$timestamp" '+%Y' 2>/dev/null) year=$($_DATE_CMD -d "@$timestamp" '+%Y' 2>/dev/null)
else else
# Default case can get funky. We need to create a clever regex to # Default case can get funky. We need to create a clever regex to
# handle the default case which is like Mon Oct 15 12:34:56 2023 +0000 # handle the default case which is like Mon Oct 15 12:34:56 2023 +0000
@ -1107,7 +1113,7 @@ function parse_year() {
date_str=$(echo "$date_str" | awk -F'/' '{print $2"/"$1"/"$3}') date_str=$(echo "$date_str" | awk -F'/' '{print $2"/"$1"/"$3}')
fi fi
# Extract the final date # Extract the final date
year=$(date -d "$date_str" '+%Y' 2>/dev/null) year=$($_DATE_CMD -d "$date_str" '+%Y' 2>/dev/null)
fi fi
echo "$year" echo "$year"
@ -1403,7 +1409,7 @@ if [[ "$#" -eq 1 ]]; then
# what people may try. This script doesn't provide any additional # what people may try. This script doesn't provide any additional
# output for a bad date since `date`'s STDERR already contains # output for a bad date since `date`'s STDERR already contains
# useful information. # useful information.
if ! date -d "${newDate}" +%s > /dev/null 2>&1; then if ! $_DATE_CMD -d "${newDate}" +%s > /dev/null 2>&1; then
newDate="" newDate=""
fi fi
done done
@ -1500,7 +1506,7 @@ if [[ "$#" -eq 0 ]]; then
11) newDate="" 11) newDate=""
while [[ -z "${newDate}" ]]; do while [[ -z "${newDate}" ]]; do
read -r -p "Since what date? (e.g. '2023-04-13', '13 April 2023', 'last Thursday') " newDate 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 if ! $_DATE_CMD -d "${newDate}" +%s > /dev/null 2>&1; then
newDate="" newDate=""
fi fi
done done

View file

@ -17,6 +17,24 @@ 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.
.PP .PP
.SH PLATFORM NOTES
.PP
git-quick-stats requires GNU date support for date parsing options.
.PP
On macOS, install GNU coreutils with:
.PP
.B brew install coreutils
.PP
On FreeBSD, install GNU coreutils with:
.PP
.B pkg install coreutils
.PP
GNU date may be available as either
.B date
or
.B gdate
and git-quick-stats auto-detects both.
.PP
.SH GENERATE OPTIONS .SH GENERATE OPTIONS
.PP .PP
\fB\-h\fR, \-?, \fB\-\-help\fR \fB\-h\fR, \-?, \fB\-\-help\fR