#!/usr/bin/env bash # usage info usage() { cat <&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 ;; -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 correctly installed. if ! which lesskey &>/dev/null; then echo "abort: the program lesskey is not installed. Make sure to install it before running git-recall" 1>&2 exit 1 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" # Log command. GIT_LOG="git log --pretty=format:'${GIT_FORMAT}' --author \"$AUTHOR\" --since \"$SINCE\" --abbrev-commit" # 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's number of lines. CN=$(tput cols) # Screen's number of columns. TN=$(( $NI < $((SN -1)) ? $NI : $((SN -1)))) # Number of lines that we will display. OFFSET=0 #Incremented by one each time a commit's length is higher than teminal width. # 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 # Add +1 to OFFSET if a commit's length is bigger than the current terminal session's width. (This is to fix a redraw issue) for C in "${COMMITS[@]}" do ELT="$(echo "$C" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")" # remove colors escape codes if [[ ${#ELT} -gt $CN ]]; then OFFSET=$(( OFFSET + 1 )) fi done # 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) echo "\t quit" | lesskey -o $HOME/.lsh_less_keys_tmp -- - &> /dev/null ## Get commit's diff. function get_diff() { ELT="$(echo "${COMMITS[$CP-1]}" | sed -r "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 echo -e "$REVERSE ${COMMITS[$CP - 1]}\n\n $DIFF" | less -r -k $HOME/.lsh_less_keys_tmp 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 + OFFSET )) && tput ed [[ $END = true ]] && tput cuu 1 fi } # Calculate OFFSET to avoid bad redraw. (Really bad performance.) function calculate_offset { # Add +1 to OFFSET if a commit's length is bigger than the current terminal session's width. (This is to fix a redraw issue) OFFSET=0 limit=$(( SN + SI )) tmp=("${COMMITS[@]:$SI:$limit}") for C in "${tmp[@]}" do ELT="$(echo "$C" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")" # remove colors escape codes if [[ ${#ELT} -gt $CN ]]; then OFFSET=$(( OFFSET + 1 )) fi done } { # 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 # Set last index to print. (based on OFFSET) if [[ $TN == $NI ]]; then END_INDEX=$TN elif [[ $TN == $(( SN - 1 )) ]]; then # Calculate new OFFSET. if [[ $OFFSET != 0 ]]; then calculate_offset #This involve really bad performance. Should be reconsidered. fi END_INDEX=$(( TN + SI -1 - OFFSET )) fi # Loop and echo commits for i in `seq $SI $END_INDEX` 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 - OFFSET )) && SI=$(( NI - SN + 2 )) [[ $CP == 0 ]] && [[ $SI=1 ]] && [[ $TN == $NI ]] && CP=$TN [[ $CP == $(( SI - 1 )) ]] && [[ $SI != 1 ]] && SI=$(( SI - 1 )) [[ $TN != $(( SN - 1 )) ]] && tput cuu $(( TN + OFFSET )) [[ $TN == $(( SN - 1 )) ]] && tput cuu $(( SN - 1 )) [[ $SI != 1 ]] && tput ed # clear screen ;; "$ad" | "$ad_1") CP=$(( CP + 1 )) [[ $CP == $(( NI + 1 )) ]] && CP=1 && SI=1 [[ $CP == $(( SN + SI - 1 - OFFSET )) ]] && [[ $TN == $(( SN - 1 )) ]] && SI=$(( SI + 1 )) [[ $TN != $(( SN - 1 )) ]] && tput cuu $(( TN + OFFSET )) [[ $TN == $(( SN - 1 )) ]] && tput cuu $(( SN - 1 )) [[ $SI != 1 ]] && tput ed # clear screen [[ $SI = 1 ]] && [[ $CP = 1 ]] && tput ed # clear screen ;; "$nl") tput cuu $(( TN + OFFSET )) print_diff ;; "q") si=false END=true tput cuu 1 #move cursor up one line. (remove extra line) ;; * ) tput cuu $(( TN + OFFSET )) esac done rm $HOME/.lsh_less_keys_tmp # remove temporary less keybindings tput cnorm # unhide cursor echo "$NORMAL" # normal colors } >&2 # END capture