#!/usr/bin/env bash # usage info usage() { cat </dev/null; then echo "abort: not a git repository." 1>&2 exit 1 fi # Parse options while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in -v | --version ) echo $version exit ;; -d | --date ) SINCE="$2 days ago" shift; ;; -a | --author ) AUTHOR="$2" shift ;; -f | --fetch ) FETCH=true ;; -l | --local ) TEMPLATE="local" ;; -h | --help ) usage exit ;; * ) echo "abort: unknown argument" 1>&2 exit 1 esac shift done if [[ "$1" == "--" ]]; then shift; fi # Colored output. function colored() { GREEN=$(tput setaf 4) YELLOW=$(tput setaf 3) NORMAL=$(tput sgr0) REVERSE=$(tput rev) } # Uncolored output. function uncolored() { GREEN="" YELLOW="" NORMAL="" REVERSE="" } # Enable colors if supported by terminal. if [[ -t 1 ]] && [[ -n "$TERM" ]] && which tput &>/dev/null && tput colors &>/dev/null; then ncolors=$(tput colors) if [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then colored else uncolored fi else uncolored fi # Check if lesskey is installed. if command -v lesskey &> /dev/null; then LESSKEY=true fi # Set AUTHOR to current user if no param passed or display for all users if param equal to "all". if [[ ! -n $AUTHOR ]]; then AUTHOR=$(git config user.name 2>/dev/null) elif [[ $AUTHOR = "all" ]]; then AUTHOR=".*" fi # Fetch changes before. if [[ $FETCH == true ]]; then echo "${GREEN}Fetching changes...${NORMAL}" git fetch --all &> /dev/null tput cuu1 tput ed # clear screen fi # Log template. GIT_FORMAT="%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" # Get currently tracked branch function get_current_branch() { if ! CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD); then echo "abort" 1>&2 exit 1 fi } # Log command templates function set_log_command() { if [ "$TEMPLATE" == "local" ]; then get_current_branch GIT_LOG="git log --pretty=format:'${GIT_FORMAT}' --abbrev-commit origin/${CURRENT_BRANCH}..HEAD " else GIT_LOG="git log --pretty=format:'${GIT_FORMAT}' --author \"$AUTHOR\" --since \"$SINCE\" --abbrev-commit" fi } set_log_command # Change temporary the IFS to store GIT_LOG's output into an array. IFS=$'\n' COMMITS=($(eval ${GIT_LOG} 2>/dev/null)) unset IFS NI=${#COMMITS[@]} # Total number of items. SN=$(( `tput lines` - 1 )) # Screen number of lines. TN=$(( $NI < $((SN -1)) ? $NI : $((SN -1)))) # Number of lines that we will display. # If there is no items, exit. if [[ $NI = 0 ]]; then if [[ $AUTHOR = ".*" ]]; then echo "${YELLOW}All contributors did nothing during this period.${NORMAL}" && exit 0 else echo "${YELLOW}The contributor \"${AUTHOR}\" did nothing during this period.${NORMAL}" && exit 0 fi fi # Set keys. au="`echo -e '\e[A'`" # arrow up au_1="k" # arrow up ad="`echo -e '\e[B'`" # arrow down ad_1="j" # arrow down ec="`echo -e '\e'`" # escape nl="`echo -e '\n'`" # newline # Create a temporary lesskey file to change the keybindings so the user can use the TAB key to quit less. (more convenient) if [[ $LESSKEY = true ]]; then echo "\t quit" | lesskey -o $HOME/.lsh_less_keys_tmp -- - &> /dev/null fi ## Get commit's diff. function get_diff() { case "$OSTYPE" in darwin*) SED_BIN="sed -E" ;; *) SED_BIN="sed -r" ;; esac ELT="$(echo "${COMMITS[$CP-1]}" | $SED_BIN "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")" # remove colors escape codes DIFF_TIP=${ELT:0:7} DIFF_CMD="git show $DIFF_TIP --color=always" DIFF=$(eval ${DIFF_CMD} 2>/dev/null) DIFF_LINES_NUMBER="$(echo "$DIFF" | wc -l)" } ## This function will print the diff according the commit's tip. If the diff is too long, the result will be displayed using 'less'. function print_diff() { get_diff if [[ $(( TN + DIFF_LINES_NUMBER )) -ge $(( `tput lines` - 1 )) ]]; then if [[ $LESSKEY = true ]]; then echo -e "$REVERSE ${COMMITS[$CP - 1]}\n\n $DIFF" | less -r -k $HOME/.lsh_less_keys_tmp else echo -e "$REVERSE ${COMMITS[$CP - 1]}\n\n $DIFF" | less -r fi else stop=false tput ed for i in `seq 1 $TN` do echo -n "$NORMAL" [[ $CP == $i ]] && echo -n "$REVERSE" echo "${COMMITS[$i - 1]}" if [[ $CP == $i ]]; then echo "$DIFF" fi done while ! $stop do read -sn 1 key case "$key" in "$nl") stop=true ;; "q") stop=true END=true ;; esac done [[ $END = false ]] && tput cuu $(( TN + DIFF_LINES_NUMBER )) && tput ed [[ $END = true ]] && tput cuu 1 fi } { # capture stdout to stderr tput civis # hide cursor. CP=1 # current position SI=1 # index END=false # end while loop ## Loops, reads inputs and prints commits until user presses 'q' to exit or TAB to show the diff. while ! $END do for i in `seq $SI $(( $TN + $SI -1 ))` do echo -n "$NORMAL" [[ $CP == $i ]] && echo -n "$REVERSE" echo "${COMMITS[$i - 1]}" done read -sn 1 key [[ "$key" == "$ec" ]] && { read -sn 2 k2 key="$key$k2" } case "$key" in "$au" | "$au_1") CP=$(( CP - 1 )) [[ $CP == 0 ]] && [[ $SI=1 ]] && [[ $TN == $(( SN - 1 )) ]] && CP=$NI && SI=$(( NI - SN + 2 )) [[ $CP == 0 ]] && [[ $SI=1 ]] && [[ $TN == $NI ]] && CP=$TN [[ $CP == $(( SI - 1 )) ]] && [[ $SI != 1 ]] && SI=$(( SI - 1 )) tput cuu $TN [[ $SI != 1 ]] && tput ed # clear screen ;; "$ad" | "$ad_1") CP=$(( CP + 1 )) [[ $CP == $(( NI + 1 )) ]] && CP=1 && SI=1 [[ $CP == $(( SN + SI - 1)) ]] && [[ $TN == $(( SN - 1 )) ]] && SI=$(( SI + 1 )) tput cuu $TN [[ $SI != 1 ]] && tput ed # clear screen [[ $SI = 1 ]] && [[ $CP = 1 ]] && tput ed # clear screen ;; "$nl") tput cuu $TN print_diff ;; "q") si=false END=true tput cuu 1 ;; * ) tput cuu $TN esac done # remove temporary less keybindings [[ $LESSKEY = true ]] && rm $HOME/.lsh_less_keys_tmp tput cnorm # unhide cursor echo "$NORMAL" # normal colors } >&2 # END capture