Files
dotfiles/.config/zsh/autoload/git/glog
Julian Prein ed4dac6f82 glog: Make changing previews persistent
Switch to using the `change-preview()` action that was introduced in
0.29.0 (which was actually not yet released for a year when this feature
was first written). The old `preview()` is a one-off action while
`change-preview()` changes the `--preview` option.

This had the downside that when changing to a different preview and
moving to the next commit one would had to repeat the change. This was
especially annoying when looking through the history of a file that was
renamed. With the commit that renamed the file all previous commits
broke in the `files_only` preview as the path didn't exist yet (A
possible but probably pretty hard TODO to fix).

TODO: glog: Fix files_only preview for renamed files or give prompt to
      change the paths
2024-11-01 01:30:43 +01:00

155 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env zsh
## Author: druckdev
## Created: 2020-08-28
##
## A TUI for git-log using fzf.
## Displays git-log in fzf and git-show as preview command for each commit.
# TODO: preview breaks when files were passed but they were renamed and have a
# different name at the point of this commit
# extendedglob is necessary for the expansion of the binds array
emulate -L zsh -o extendedglob
# Return if fzf is not available
if (( ! $+commands[fzf] )); then
printf "command not found: fzf" >&2
return 1
fi
# Return if not in git repo
git rev-parse || return
# One line format for fzf list view
# abbreviated commit hash (yellow), title and ref names
local formatshort='--pretty=format:%C(yellow)%h %Creset%s%C(auto)%d'
# Verbose format for the preview window on the right
# This array is stitched together with newlines later
local -a format=(
'--pretty=format:%C(yellow)' # newline created by this eaten by %-
'%-Commit: %H%C(auto)' # yellow commit hash
' %D%Cblue' # auto colored ref names (if any)
'Author: %aN <%aE>%Cred' # blue author mail
'AuthorDate: %ad%Cblue' # red author date
'Commit: %cN <%cE>%Cred' # blue commiter mail
'CommitDate: %cd%Cblue' # red commit date
'Signer: %GS%Cgreen' # signer name
'Key (%G?): %GK' # pgp key used to sign
'%Creset%C(bold)' # empty line
' %s%Creset' # bold white subject
'' # newline
'%-b' # body
'' # newline
)
# Ignore the graph part at the beginning, then capture the commit hash and throw
# away the rest of the line.
local commit_hash='s/^[^a-f0-9]*([a-f0-9]*).*$/\1/'
local dateshort='--date=format:%F' # year
local date="$dateshort %T %z" # year time timezone
local -A fzf_preview
read -r -d '' <<EOT
out="\$(echo -E {} | sed -E "$commit_hash")"; [[ -z "\$out" ]] ||
EOT
fzf_preview[construct]="$REPLY"
read -r -d '' <<EOT
git show "${(j:%n:)format}" "$date" --color=always --patch-with-stat "\$out"
EOT
fzf_preview[patch]="$fzf_preview[construct] { $REPLY"
# Get file arguments after (and including) `--` and wrap each in double quotes
# TODO: Display `...` behind patch-stat if the patch touched more files
# TODO: Use `-O <file>` for 'patch' command with `-- <file>` argument, so that
# the passed file is ontop although the full patch is shown
# TODO: Support -L flag as well (add `-s` for list and `-- <file>` & maybe `-W`
# for preview)
fzf_preview[files_only]="$fzf_preview[patch] ${(@)${@:${@[(ei)--]}}/(#m)*/\"$MATCH\"}"
# Use git's pager in the preview window (and with it any special highlighting
# tool, such as diff-so-fancy)
local pager
pager="$(git config --get --default="" core.pager)"
fzf_preview[patch]+="${pager:+ | }$pager; }"
fzf_preview[files_only]+="${pager:+ | }$pager; }"
read -r -d '' <<EOT
git show "${(j:%n:)format}" "$date" --color=always --stat "\$out"
EOT
fzf_preview[stat]="$fzf_preview[construct] { $REPLY; }"
# Put the commit hash into the clipboard
# (If no known clipboard tool is available, just print it)
local fzf_copy_command="$fzf_preview[construct] echo -n \"\$out\""
if [[ $OSTYPE =~ darwin ]] && (( $+commands[pbcopy] )); then
fzf_copy_command+=" | pbcopy"
elif (( $+commands[xclip] )); then
fzf_copy_command+=" | xclip -selection c"
fi
local -A binds=(
# scroll in preview window
"ctrl-alt-j" "preview-down"
"ctrl-alt-k" "preview-up"
# Copy commit hash
"ctrl-y" "execute@$fzf_copy_command@"
# Open preview "fullscreen"
# NOTE: `-+F` is there to negate a possible --quit-if-one-screen
# TODO: This assumes less to be used in core.pager
"enter" "execute@$fzf_preview[patch] | $pager -+F@"
# Preview stats
"ctrl-s" "change-preview($fzf_preview[stat])"
# Preview patch
"ctrl-p" "change-preview($fzf_preview[patch])"
# Files only
"ctrl-f" "change-preview($fzf_preview[files_only])"
)
# TODO: Make the --preview argument dependent of --stat flag (i.e.
# fzf_preview[stat] in this case). It does not really make sense to pass
# it to `git log` but can be an indicator for the preview function
local -a fzf_args=(
# Understand ansi color escape sequences.
"--ansi"
# Expand the binds array in the format "key1:value1,key2:value2".
"--bind" "${(@kj:,:)binds/(#m)*/$MATCH:$binds[$MATCH]}"
# Execute git show on the commit as preview.
"--preview" "$fzf_preview[files_only]"
# Reverse the layout so that the newest commit is at the top.
"--reverse"
# Do not sort when typing to maintain the sorting by date.
"--no-sort"
)
# The preview-window should be placed differently depending on the dimensions of
# the terminal. It should have at least 152 columns to fit a preview window on
# the right (12 hash, 50 subject, 80 patch, 10 git graph and fzf ui).
#
# ctrl-space should cycle through the preview-window positions, starting with
# hidden, then not the start position and than back the start position.
local -a tty_size
tty_size=(${=$(command stty size 2>/dev/null)})
if (( ! $? )) && (( $tty_size[2] > 152 )); then
fzf_args+=(
--preview-window=right
--bind "ctrl-space:change-preview-window(hidden|bottom|right)"
)
else
fzf_args+=(
--preview-window=down
--bind "ctrl-space:change-preview-window(hidden|right|bottom)"
)
fi
# Display the commits in the above format and pipe that into fzf if stdout is a
# terminal.
if [ -t 1 ]; then
git log "$formatshort" "$dateshort" --color=always "$@" \
| env LESS="$LESS${LESS:+ }-+F" fzf "${fzf_args[@]}"
else
git log "$formatshort" "$dateshort" --color=always "$@"
fi
return 0