diff --git a/git-recall b/git-recall new file mode 100644 index 0000000..884287c --- /dev/null +++ b/git-recall @@ -0,0 +1,254 @@ +#!/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 + +# 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 number of lines. +TN=$(( $NI < $((SN -1)) ? $NI : $((SN -1)))) # Number of lines that we will display. + +# If there is no items, exit. +[[ $NI = 0 ]] && echo "${YELLOW}The contributor \"${AUTHOR}\" did nothing during this period.${NORMAL}" && exit 0 + +# 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 -- - + +## 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 )) && 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 + +rm $HOME/.lsh_less_keys_tmp # remove temporary less keybindings +tput cnorm # unhide cursor +echo "$NORMAL" # normal colors + +} >&2 # END capture +