Skip to content

Commit

Permalink
Feature: add forgit show command (#417)
Browse files Browse the repository at this point in the history
Introducing `show` as a new subcommand of `forgit`, an interactive
version of `git show`. The configured default alias is `gso`.

Alt-T can be used to toggle between showing the diff and the commit
message in the fzf preview window.

The new command in used instead of `forgit diff` when pressing enter
in `forgit log`. This fixes the display of diffs for merge commits.

**Note**: we have a requirement for the `fzf` version now. The minimum
required `fzf` version is 0.49.0. If the installed version is lower,
forgit will exit with an error message.

Fixes #416.
  • Loading branch information
carlfriedrich authored Jan 29, 2025
1 parent fdae9cb commit bb83b3c
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 2 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ It's **lightweight** and **easy to use**.

# 📥 Installation

*Make sure you have [`fzf`](https://github.com/junegunn/fzf) installed.*
## Requirements

- [`fzf`](https://github.com/junegunn/fzf) version `0.49.0` or higher

If your OS package manager bundles an older version of `fzf`, you might install it using [`fzf`'s own install script'](https://github.com/junegunn/fzf?tab=readme-ov-file#using-git).

## Shell package managers

``` zsh
# for zplug
Expand Down Expand Up @@ -101,6 +107,8 @@ Then add the following to your shell's config file:

- **Interactive `git diff` viewer** (`gd`)

- **Interactive `git show` viewer** (`gso`)

- **Interactive `git reset HEAD <file>` selector** (`grh`)

- **Interactive `git checkout <file>` selector** (`gcf`)
Expand Down Expand Up @@ -172,6 +180,7 @@ You can change the default aliases by defining these variables below.
forgit_log=glo
forgit_reflog=grl
forgit_diff=gd
forgit_show=gso
forgit_add=ga
forgit_reset_head=grh
forgit_ignore=gi
Expand Down Expand Up @@ -232,6 +241,7 @@ These are passed to the according `git` calls.
| `glo` | `FORGIT_LOG_GIT_OPTS` |
| `grl` | `FORGIT_REFLOG_GIT_OPTS` |
| `gd` | `FORGIT_DIFF_GIT_OPTS` |
| `gso` | `FORGIT_SHOW_GIT_OPTS` |
| `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` |
| `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` |
| `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` |
Expand Down Expand Up @@ -286,6 +296,7 @@ Customizing fzf options for each command individually is also supported:
| `grl` | `FORGIT_REFLOG_FZF_OPTS` |
| `gi` | `FORGIT_IGNORE_FZF_OPTS` |
| `gd` | `FORGIT_DIFF_FZF_OPTS` |
| `gso` | `FORGIT_SHOW_FZF_OPTS` |
| `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` |
| `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` |
| `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` |
Expand Down
98 changes: 97 additions & 1 deletion bin/git-forgit
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
# This gives users the choice to set aliases inside of their git config instead
# of their shell config if they prefer.

# Check if fzf is installed
installed_fzf_version=$(fzf --version 2>/dev/null | awk '{print $1}')
if [[ -z "$installed_fzf_version" ]]; then
echo "fzf is not installed. Please install fzf first."
exit 1
fi

# Check fzf version
required_fzf_version="0.49.0"
higher_fzf_version=$(printf '%s\n' "$required_fzf_version" "$installed_fzf_version" | sort -V | tail -n1)
if [[ "$higher_fzf_version" != "$installed_fzf_version" ]]; then
echo "fzf version $required_fzf_version or higher is required. You have $installed_fzf_version."
exit 1
fi

# Set shell for fzf preview commands
# Disable shellcheck for "which", because it suggests "command -v xxx" instead,
# which is not a working replacement.
Expand Down Expand Up @@ -185,7 +200,7 @@ _forgit_log_enter() {
local sha
sha=$(echo "$1" | _forgit_extract_sha)
shift
echo "$sha" | xargs -I% "${FORGIT}" diff %^! "$@"
echo "$sha" | xargs -I% "${FORGIT}" show % "$@"
}

# git commit viewer
Expand Down Expand Up @@ -339,6 +354,83 @@ _forgit_diff() {
return $fzf_exit_code
}

_forgit_exec_show() {
_forgit_show_git_opts=()
_forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS"
git show --pretty="" --diff-merges=first-parent --color=always "${_forgit_show_git_opts[@]}" "$@"
}

_forgit_show_view() {
local input_line=$1
local diff_context=$2
local commit=$3
local repo
repo=$(git rev-parse --show-toplevel)
cd "$repo" || return 1
echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \
"$FORGIT" exec_show "${commit}^{commit}" -U"$diff_context" -- | _forgit_pager diff
}

_forgit_show_preview() {
local input_line=$1
local diff_context=$2
local commit=$3
if [[ "$FZF_PREVIEW_LABEL" =~ "Diff" ]]; then
_forgit_show_view "${input_line}" "${diff_context}" "${commit}"
else
git show --quiet --color=always "${FZF_PROMPT%% *}"
fi
}

_forgit_show_enter() {
file=$1
commit=$2
_forgit_show_view "$file" "$_forgit_fullscreen_context" "${commit}"
}

# git show viewer
_forgit_show() {
_forgit_inside_work_tree || return 1
local files opts commit escaped_commit
files=()
if [[ $# -ne 0 ]]; then
if git rev-parse "$1" -- &>/dev/null ; then
commit="$1" && files=("${@:2}")
else
commit="HEAD" && files=("$@")
fi
else
commit="HEAD"
fi
# Escape opening brackets to support stashes (see comment in _forgit_diff)
escaped_commit=${commit//\{/\\\\\{}
opts="
$FORGIT_FZF_DEFAULT_OPTS
+m -0 --bind=\"enter:execute($FORGIT show_enter {} $escaped_commit | $FORGIT pager enter)\"
--preview=\"$FORGIT show_preview {} '$_forgit_preview_context' $escaped_commit\"
--preview-label=\" Diff \"
--bind=\"alt-e:execute($FORGIT edit_diffed_file {})+refresh-preview\"
--bind=\"alt-t:transform:[[ ! \\\"\$FZF_PREVIEW_LABEL\\\" =~ 'Diff' ]] &&
echo 'change-preview-label( Diff )+refresh-preview' ||
echo 'change-preview-label( Commit Message )+refresh-preview'\"
$FORGIT_DIFF_FZF_OPTS
--prompt=\"${commit} > \"
"
_forgit_show_git_opts=()
_forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS"
# Add "^{commit}" suffix after the actual commit. This suppresses the tag information in case it is a tag.
# See: https://git-scm.com/docs/git-show#Documentation/git-show.txt-codegitshow-s--formatsv100commitcode
git show --pretty="" --name-status --diff-merges=first-parent "${_forgit_show_git_opts[@]}" "${commit}^{commit}" \
-- "${files[@]}" |
sed -E 's/^([[:alnum:]]+)[[:space:]]+(.*)$/[\1] \2/' |
sed 's/ / -> /2' | expand -t 8 |
FZF_DEFAULT_OPTS="$opts" fzf
fzf_exit_code=$?
# exit successfully on 130 (ctrl-c/esc)
[[ $fzf_exit_code == 130 ]] && return 0
return $fzf_exit_code
}

_forgit_add_preview() {
file=$(echo "$1" | _forgit_get_single_file_from_add_line)
if (git status -s -- "$file" | grep '^??') &>/dev/null; then # diff with /dev/null for untracked files
Expand Down Expand Up @@ -1021,6 +1113,7 @@ public_commands=(
"rebase"
"reset_head"
"revert_commit"
"show"
"stash_show"
"stash_push"
)
Expand All @@ -1035,10 +1128,13 @@ private_commands=(
"cherry_pick_preview"
"clean_preview"
"diff_enter"
"exec_show"
"file_preview"
"ignore_preview"
"revert_preview"
"reset_head_preview"
"show_enter"
"show_preview"
"stash_push_preview"
"stash_show_preview"
"yank_sha"
Expand Down
2 changes: 2 additions & 0 deletions completions/_git-forgit
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ _git-forgit() {
reset_head) _git-staged ;;
revert_commit) __git_recent_commits ;;
stash_show) _git-stash-show ;;
show) _git-show ;;
esac
}

Expand All @@ -122,6 +123,7 @@ compdef _git-rebase forgit::rebase
compdef _git-staged forgit::reset::head
compdef __git_recent_commits forgit::revert::commit
compdef _git-stash-show forgit::stash::show
compdef _git-show forgit::show

# this is the case of calling the command and pressing tab
# the very first time of a shell session, we have to manually
Expand Down
4 changes: 4 additions & 0 deletions completions/git-forgit.bash
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ _git_forgit()
rebase
reset_head
revert_commit
show
stash_show
stash_push
"
Expand All @@ -101,6 +102,7 @@ _git_forgit()
rebase) _git_rebase ;;
reset_head) _git_reset ;;
revert_commit) _git_revert ;;
show) _git_show ;;
stash_show) _git_stash_show ;;
esac
;;
Expand Down Expand Up @@ -135,6 +137,7 @@ then
__git_complete forgit::rebase _git_rebase
__git_complete forgit::reset::head _git_reset
__git_complete forgit::revert::commit _git_revert
__git_complete forgit::show _git_show
__git_complete forgit::stash::show _git_stash_show

# Completion for forgit plugin shell aliases
Expand All @@ -154,6 +157,7 @@ then
__git_complete "${forgit_rebase}" _git_rebase
__git_complete "${forgit_reset_head}" _git_reset
__git_complete "${forgit_revert_commit}" _git_revert
__git_complete "${forgit_show}" _git_show
__git_complete "${forgit_stash_show}" _git_stash_show
fi
fi
1 change: 1 addition & 0 deletions completions/git-forgit.fish
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a reflog -d 'git reflo
complete -c git-forgit -n __fish_forgit_needs_subcommand -a rebase -d 'git rebase'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a reset_head -d 'git reset HEAD (unstage) selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a revert_commit -d 'git revert commit selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a show -d 'git show viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git stash viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_push -d 'git stash push selector'

Expand Down
1 change: 1 addition & 0 deletions conf.d/forgit.plugin.fish
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if test -z "$FORGIT_NO_ALIASES"
abbr -a -- (string collect $forgit_log; or string collect "glo") git-forgit log
abbr -a -- (string collect $forgit_reflog; or string collect "grl") git-forgit reflog
abbr -a -- (string collect $forgit_diff; or string collect "gd") git-forgit diff
abbr -a -- (string collect $forgit_show; or string collect "gso") git-forgit show
abbr -a -- (string collect $forgit_ignore; or string collect "gi") git-forgit ignore
abbr -a -- (string collect $forgit_checkout_file; or string collect "gcf") git-forgit checkout_file
abbr -a -- (string collect $forgit_checkout_branch; or string collect "gcb") git-forgit checkout_branch
Expand Down
6 changes: 6 additions & 0 deletions forgit.plugin.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ forgit::diff() {
"$FORGIT" diff "$@"
}

forgit::show() {
"$FORGIT" show "$@"
}

forgit::add() {
"$FORGIT" add "$@"
}
Expand Down Expand Up @@ -146,6 +150,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
export forgit_log="${forgit_log:-glo}"
export forgit_reflog="${forgit_reflog:-grl}"
export forgit_diff="${forgit_diff:-gd}"
export forgit_show="${forgit_show:-gso}"
export forgit_ignore="${forgit_ignore:-gi}"
export forgit_checkout_file="${forgit_checkout_file:-gcf}"
export forgit_checkout_branch="${forgit_checkout_branch:-gcb}"
Expand All @@ -166,6 +171,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
alias "${forgit_log}"='forgit::log'
alias "${forgit_reflog}"='forgit::reflog'
alias "${forgit_diff}"='forgit::diff'
alias "${forgit_show}"='forgit::show'
alias "${forgit_ignore}"='forgit::ignore'
alias "${forgit_checkout_file}"='forgit::checkout::file'
alias "${forgit_checkout_branch}"='forgit::checkout::branch'
Expand Down

0 comments on commit bb83b3c

Please sign in to comment.