From d4e56cf557234d5df0a596fe74b10cf0b23382f4 Mon Sep 17 00:00:00 2001 From: Tom Ice Date: Tue, 1 Oct 2024 20:11:26 -0400 Subject: [PATCH] Adjust commitsByYear() to support multiple dates * commitsByYear() previously could only support Default-style dates for git log. However, a user can use a variety of different date formats such as relative, default, iso, rfc, short, and raw. This change modifies commitsByYear() to handle multiple different date strings Fixes #172 --- git-quick-stats | 97 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/git-quick-stats b/git-quick-stats index 139b16f..342a067 100755 --- a/git-quick-stats +++ b/git-quick-stats @@ -17,7 +17,7 @@ _since=${_GIT_SINCE:-} if [[ -n "${_since}" ]]; then _since="--since=$_since" else - _since="--since=$(git log --reverse --format='%ad' | head -n1)" + _since="--since=$(git log --reverse --format='%ad' --date=iso | head -n1)" fi # End of git log date. Respects all git datetime formats @@ -761,6 +761,47 @@ function commitsPerDay() { --date=short --format='%ad' $_log_options $_pathspec | sort | uniq -c } +################################################################################ +# DESC: Convert a timestamp to a date string to handle git's date formats +# ARGS: $1: Timestamp +# OUTS: Echoes a four-digit year +################################################################################ +function parse_year() { + local date_str="$1" + local year + local timestamp + local default_git_date_regex + + # Handle the raw UNIX timestamp format i.e. 1697375696 +0000 + if [[ "$date_str" =~ ^[0-9]+(\ [+-][0-9]{4})?$ ]]; then + timestamp=$(echo "$date_str" | awk '{print $1}') + year=$(date -d "@$timestamp" '+%Y' 2>/dev/null) + else + # 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 + # Let's make this explicit for future devs to follow along. + default_git_date_regex='^' # Start from the beginning of the string + default_git_date_regex+='[A-Za-z]{3}\ ' # Day abbrev + default_git_date_regex+='[A-Za-z]{3}\ ' # Month abbrev + default_git_date_regex+='[0-9]{1,2}\ ' # Day of the month + default_git_date_regex+='[0-9]{2}:[0-9]{2}:[0-9]{2}\ ' # Time HH:MM:SS + default_git_date_regex+='[0-9]{4}\ ' # Year + default_git_date_regex+='[+-][0-9]{4}$' # Timezone offset + + if [[ "$date_str" =~ $default_git_date_regex ]]; then + # Move the year before the time to match a format that Date can parse + date_str=$(echo "$date_str" | awk '{print $1, $2, $3, $5, $4, $6}') + elif [[ "$date_str" =~ ^[0-9]{1,2}/[0-9]{1,2}/[0-9]{2,4} ]]; then + # Handle DD/MM/YYYY format + date_str=$(echo "$date_str" | awk -F'/' '{print $2"/"$1"/"$3}') + fi + # Extract the final date + year=$(date -d "$date_str" '+%Y' 2>/dev/null) + fi + + echo "$year" +} + ################################################################################ # DESC: Displays a horizontal bar graph based on total commits per year # ARGS: None @@ -769,30 +810,38 @@ function commitsPerDay() { function commitsByYear() { optionPicked "Git commits by year:" local year startYear endYear __since __until - startYear=$(echo "$_since" | sed -E 's/^.* ([0-9]{4})( .*)?$/\1/') - endYear=$(echo "$_until" | sed -E 's/^.* ([0-9]{4})( .*)?$/\1/') + # Extract the date strings from $_since and $_until + since_date="${_since#--since=}" + until_date="${_until#--until=}" + + # Grab the four digit year from $_since and $_until + startYear=$(parse_year "$since_date") + endYear=$(parse_year "$until_date") + echo -e "\tyear\tsum" - for year in $(seq "$startYear" "$endYear") - do - if [ "$year" = "$startYear" ] - then - __since=$_since - __until="--until=$year-12-31" - elif [ "$year" = "$endYear" ] - then - __since="--since=$year-01-01" - __until=$_until + # Add time strings to make these a touch more robust + for year in $(seq "$startYear" "$endYear"); do + if [[ "$year" = "$startYear" ]]; then + __since=$_since + __until="--until=$year-12-31 23:59:59" + elif [[ "$year" = "$endYear" ]]; then + __since="--since=$year-01-01 00:00:00" + __until=$_until else - __since="--since=$year-01-01" - __until="--until=$year-12-31" + __since="--since=$year-01-01 00:00:00" + __until="--until=$year-12-31 23:59:59" fi - - echo -en "\t$year\t" - git -c log.showSignature=false shortlog -n $_merges --format='%ad %s' \ - "$__since" "$__until" $_log_options | grep -cE \ - " \w\w\w [0-9]{1,2} [0-9][0-9]:[0-9][0-9]:[0-9][0-9] $year " \ - || continue + + # Count commits directly using git rev-list instead of git log + commit_count=$( + git rev-list --count $_merges \ + "$__since" "$__until" HEAD $_log_options + ) + echo -e "\t$year\t$commit_count" + # TODO: The bar graph can get funky when there are only a handful of + # commits. We can set a max length to try to fix this, but this is a + # bit of a problem across all the bar graphs. done | awk '{ count[$1] = $2 total += $2 @@ -986,7 +1035,11 @@ function suggestReviewers() { checkUtils # Check if we are currently in a git repo. -git rev-parse --is-inside-work-tree > /dev/null +if ! git rev-parse --is-inside-work-tree > /dev/null; then + echo "ERROR: You need to be inside a git repo to parse stats!" + usage + exit 1 +fi # Parse non-interative commands if [[ "$#" -eq 1 ]]; then