mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 07:26:17 -04:00
* Format comparisons and functions to be consistent * Add `checkstyle.py` script and add check to CI * Fix Ruff lints
302 lines
8.6 KiB
Bash
Executable file
302 lines
8.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
# Code modified from https://github.com/kamranahmedse/git-standup,
|
|
# under the MIT LICENSE.
|
|
usage() {
|
|
cat <<EOS
|
|
Usage:
|
|
git standup [-a <author name>] [-w <weekstart-weekend>|-d <days-ago>] [-m <max-dir-depth>] [-D date-format] [-L] [-h] [-f] [-B] [-n <number-of-commits] [-F <gpg|authordate>]
|
|
|
|
-a - Specify author to restrict search to, default to current git user.
|
|
Use "-a all" if you don't want the restriction.
|
|
-w - Specify weekday range to limit search to
|
|
-m - Specify the depth of recursive directory search
|
|
-L - Toggle inclusion of symbolic links in recursive directory search
|
|
-d - Specify the number of days back to include
|
|
-D - Specify the date format for "git log" (default: relative)
|
|
-h - Display this help screen
|
|
-f - Fetch the latest commits beforehand
|
|
-B - Display the commits in branch groups
|
|
-n - Limit the number of commits displayed per group
|
|
-F gpg - Show if commit is GPG signed (G) or not (N)
|
|
-F authordate - Print author date instead of commit date
|
|
|
|
Examples:
|
|
git standup -a "John Doe" -w "MON-FRI" -m 3
|
|
EOS
|
|
}
|
|
|
|
warn() {
|
|
>&2 echo "${BOLD}${RED}WARNING: $1${NORMAL}"
|
|
}
|
|
|
|
git rev-parse --show-toplevel > /dev/null 2>&1
|
|
in_git_repo=$?
|
|
|
|
# Use colors, but only if connected to a terminal, and that terminal
|
|
# supports them.
|
|
if command -v tput &>/dev/null; then
|
|
ncolors=$(tput colors)
|
|
fi
|
|
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
|
|
RED="$(tput setaf 1)"
|
|
GREEN="$(tput setaf 2)"
|
|
YELLOW="$(tput setaf 3)"
|
|
BOLD="$(tput bold)"
|
|
NORMAL="$(tput sgr0)"
|
|
BOLD=$(tput bold)
|
|
UNDERLINE=$(tput smul)
|
|
NORMAL=$(tput sgr0)
|
|
COLOR=always
|
|
else
|
|
RED=""
|
|
GREEN=""
|
|
YELLOW=""
|
|
BOLD=""
|
|
NORMAL=""
|
|
BOLD=""
|
|
UNDERLINE=""
|
|
NORMAL=""
|
|
COLOR=never
|
|
fi
|
|
|
|
# Only enable exit-on-error after the non-critical colorization stuff,
|
|
# which may fail on systems lacking tput or terminfo
|
|
set -e
|
|
|
|
RANGE_SPECIFIED=
|
|
COMMIT_DATE_FORMAT=%cd
|
|
USE_GPG_FORMAT=no
|
|
|
|
while getopts "hgfF:Bd:a:w:m:D:n:L" opt; do
|
|
case $opt in
|
|
h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
a)
|
|
if [[ "$OPTARG" = 'all' ]] ; then
|
|
AUTHOR=".*"
|
|
else
|
|
AUTHOR="$OPTARG"
|
|
fi
|
|
;;
|
|
d)
|
|
test -n "$RANGE_SPECIFIED" && warn "-d option is conflict with -w"
|
|
RANGE_SPECIFIED=yes
|
|
if [ "$OPTARG" -lt 1 ]; then
|
|
>&2 echo "Specify days less than one is invalid"
|
|
exit 1
|
|
fi
|
|
SINCE="$OPTARG days ago"
|
|
;;
|
|
w)
|
|
if [ -n "$RANGE_SPECIFIED" ]; then
|
|
warn "-w option is conflict with -d"
|
|
continue
|
|
fi
|
|
RANGE_SPECIFIED=yes
|
|
|
|
week_range=${OPTARG}
|
|
week_start="${week_range%%-*}"
|
|
week_start="${week_start:="Mon"}"
|
|
week_end="${week_range##*-}"
|
|
week_end=${week_end:="Fri"}
|
|
|
|
## In case it is the start of week, we need to
|
|
## show the commits since the last weekend
|
|
shopt -s nocasematch
|
|
if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]] ; then
|
|
SINCE="last $week_end";
|
|
fi
|
|
;;
|
|
f)
|
|
FETCH_LAST_COMMIT=true
|
|
;;
|
|
m)
|
|
MAXDEPTH=$((OPTARG + 1))
|
|
if [ "$MAXDEPTH" -lt 1 ]; then
|
|
>&2 echo "Specify depth less than one is invalid"
|
|
exit 1
|
|
fi
|
|
;;
|
|
L)
|
|
INCLUDE_LINKS=-L
|
|
;;
|
|
D)
|
|
GIT_DATE_FORMAT=${OPTARG}
|
|
;;
|
|
g)
|
|
warn "-g option is deprecated, use '-F gpg' instead"
|
|
USE_GPG_FORMAT=yes
|
|
;;
|
|
B)
|
|
GROUP_BY_BRANCHES=true
|
|
;;
|
|
n)
|
|
MAX_COMMIT_NUM=${OPTARG}
|
|
;;
|
|
F)
|
|
case $OPTARG in
|
|
gpg)
|
|
USE_GPG_FORMAT=yes
|
|
;;
|
|
authordate)
|
|
COMMIT_DATE_FORMAT=%ad
|
|
;;
|
|
*)
|
|
warn "Invalid argument for -F: $OPTARG"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
\?)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
shift $((OPTIND-1))
|
|
|
|
if [[ $# -gt 0 ]]; then
|
|
warn "please upgrade to new-style interface. Run 'git help standup' to get more info."
|
|
if [[ $# -gt 3 ]] ; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
AUTHOR=$1
|
|
SINCE=$2
|
|
UNTIL=$3
|
|
fi
|
|
|
|
AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
|
|
if [ -z "${AUTHOR}" ]; then
|
|
warn "please configure an author with 'git config user.name' or specify an author via '-a'"
|
|
exit 1
|
|
fi
|
|
|
|
FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
|
|
MAXDEPTH=${MAXDEPTH:=2}
|
|
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(${COMMIT_DATE_FORMAT}) %C(bold blue)<%an>%Creset"
|
|
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative}
|
|
if [[ "$USE_GPG_FORMAT" == 'yes' ]]; then
|
|
GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT %C(yellow)gpg: %G?%Creset"
|
|
fi
|
|
|
|
# Handle config of implicit week
|
|
IMPLICIT_WEEK=$(git config --get git-extras.standup.implicit-week || echo '')
|
|
if [[ -z "$RANGE_SPECIFIED" ]] && [[ -n "${IMPLICIT_WEEK}" ]]; then
|
|
week_start=${IMPLICIT_WEEK%%-*}
|
|
week_end=${IMPLICIT_WEEK##*-}
|
|
shopt -s nocasematch
|
|
if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]]; then
|
|
SINCE="last $week_end"
|
|
fi
|
|
UNTIL=today
|
|
else
|
|
SINCE=${SINCE:=yesterday}
|
|
UNTIL=${UNTIL:=today}
|
|
fi
|
|
|
|
GIT_LOG_COMMAND="git --no-pager log \
|
|
--no-merges
|
|
--since \"$SINCE\"
|
|
--until \"$UNTIL\"
|
|
--author=\"$AUTHOR\"
|
|
--abbrev-commit
|
|
--oneline
|
|
--color=$COLOR
|
|
--pretty=format:'$GIT_PRETTY_FORMAT'
|
|
--date='$GIT_DATE_FORMAT'"
|
|
if [[ -n "$MAX_COMMIT_NUM" ]]; then
|
|
GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM"
|
|
fi
|
|
|
|
git_output() {
|
|
if [ "$GROUP_BY_BRANCHES" = true ]; then
|
|
local branches
|
|
branches=$(git branch --sort=-committerdate | awk '{print substr($0, 3)}')
|
|
for branch in $branches; do
|
|
# shellcheck disable=SC2086
|
|
if output=$(eval $GIT_LOG_COMMAND "$branch"); then
|
|
if [[ -n "$output" ]] ; then
|
|
echo "${GREEN}${branch}${NORMAL}"
|
|
echo "$output"
|
|
echo ""
|
|
fi
|
|
fi
|
|
# TODO optimize:return if the latest commit of a branch is earlier than the 'since' day
|
|
done
|
|
else
|
|
# shellcheck disable=SC2086
|
|
eval $GIT_LOG_COMMAND --all
|
|
fi
|
|
}
|
|
|
|
## For when the command has been run in a non-repo directory
|
|
if [[ $in_git_repo != 0 ]]; then
|
|
## Set delimiter to newline for the loop
|
|
IFS=$'\n'
|
|
## Recursively search for git repositories
|
|
# shellcheck disable=SC2086
|
|
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
|
|
|
|
# Fetch the latest commits, if required
|
|
if [ "$FETCH_LAST_COMMIT" = true ]; then
|
|
|
|
echo "${BOLD}${GREEN}Fetching commits ..${NORMAL}"
|
|
|
|
# Foreach of the project directories, fetch the commits
|
|
for DIR in $PROJECT_DIRS; do
|
|
DIR="$(dirname "$DIR")"
|
|
pushd "$DIR" > /dev/null
|
|
|
|
if [[ -d ".git" ]] ; then
|
|
echo " ${YELLOW}$(basename "$DIR")${NORMAL}"
|
|
git fetch --all > /dev/null 2>&1
|
|
fi
|
|
|
|
popd > /dev/null
|
|
done
|
|
fi
|
|
|
|
# Get the standup details for each of the projects
|
|
for DIR in $PROJECT_DIRS; do
|
|
DIR="$(dirname "$DIR")"
|
|
pushd "$DIR" > /dev/null
|
|
## Show the detail only if it is a git repository
|
|
if [[ -d ".git" || -f ".git" ]] ; then
|
|
if GITOUT=$(git_output); then
|
|
## Only output if there is some activities
|
|
if [[ -n "$GITOUT" ]] ; then
|
|
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
|
|
echo "$GITOUT"
|
|
echo ""
|
|
fi
|
|
else
|
|
echo "Repository under $DIR could not be queried." >&2
|
|
fi
|
|
fi
|
|
popd > /dev/null
|
|
done
|
|
else
|
|
if [ "$FETCH_LAST_COMMIT" = true ]; then
|
|
echo "${GREEN}Fetching commits ..${NORMAL}"
|
|
git fetch --all > /dev/null 2>&1
|
|
fi
|
|
|
|
if GITOUT=$(git_output); then
|
|
if [[ -n "$GITOUT" ]] ; then
|
|
echo "$GITOUT"
|
|
else
|
|
if [[ $AUTHOR = '.*' ]] ; then
|
|
AUTHOR="all the contributors"
|
|
fi
|
|
|
|
echo "${YELLOW}Seems like $AUTHOR did nothing!${NORMAL}"
|
|
fi
|
|
fi
|
|
fi
|