mirror of
https://github.com/Fakerr/git-recall.git
synced 2026-09-10 07:26:42 -04:00
306 lines
6.9 KiB
Bash
306 lines
6.9 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
|
|
# usage info
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: git recall [options]
|
|
Options:
|
|
-d, --date Show logs for last n days.
|
|
-a, --author Author name.
|
|
-f, --fetch fetch commits.
|
|
-h, --help This message.
|
|
-l, --local Only show commits that have not yet been pushed to the remote branch.
|
|
-- End of options.
|
|
EOF
|
|
}
|
|
|
|
|
|
# Global variables
|
|
SINCE="1 days ago" # show logs for last day by default
|
|
AUTHOR=""
|
|
FETCH=false
|
|
GIT_FORMAT=""
|
|
GIT_LOG=""
|
|
COMMITS=""
|
|
LESSKEY=false
|
|
VERSION="1.1.1"
|
|
TEMPLATE="default"
|
|
CURRENT_BRANCH=""
|
|
|
|
# Are we in a git repo?
|
|
if [[ ! -d ".git" ]] && ! git rev-parse --git-dir &>/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
|
|
|