Skip to content

Commit bb83b3c

Browse files
Feature: add forgit show command (#417)
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.
1 parent fdae9cb commit bb83b3c

File tree

7 files changed

+123
-2
lines changed

7 files changed

+123
-2
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ It's **lightweight** and **easy to use**.
2929

3030
# 📥 Installation
3131

32-
*Make sure you have [`fzf`](https://github.com/junegunn/fzf) installed.*
32+
## Requirements
33+
34+
- [`fzf`](https://github.com/junegunn/fzf) version `0.49.0` or higher
35+
36+
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).
37+
38+
## Shell package managers
3339

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

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

110+
- **Interactive `git show` viewer** (`gso`)
111+
104112
- **Interactive `git reset HEAD <file>` selector** (`grh`)
105113

106114
- **Interactive `git checkout <file>` selector** (`gcf`)
@@ -172,6 +180,7 @@ You can change the default aliases by defining these variables below.
172180
forgit_log=glo
173181
forgit_reflog=grl
174182
forgit_diff=gd
183+
forgit_show=gso
175184
forgit_add=ga
176185
forgit_reset_head=grh
177186
forgit_ignore=gi
@@ -232,6 +241,7 @@ These are passed to the according `git` calls.
232241
| `glo` | `FORGIT_LOG_GIT_OPTS` |
233242
| `grl` | `FORGIT_REFLOG_GIT_OPTS` |
234243
| `gd` | `FORGIT_DIFF_GIT_OPTS` |
244+
| `gso` | `FORGIT_SHOW_GIT_OPTS` |
235245
| `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` |
236246
| `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` |
237247
| `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` |
@@ -286,6 +296,7 @@ Customizing fzf options for each command individually is also supported:
286296
| `grl` | `FORGIT_REFLOG_FZF_OPTS` |
287297
| `gi` | `FORGIT_IGNORE_FZF_OPTS` |
288298
| `gd` | `FORGIT_DIFF_FZF_OPTS` |
299+
| `gso` | `FORGIT_SHOW_FZF_OPTS` |
289300
| `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` |
290301
| `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` |
291302
| `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` |

bin/git-forgit

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212
# This gives users the choice to set aliases inside of their git config instead
1313
# of their shell config if they prefer.
1414

15+
# Check if fzf is installed
16+
installed_fzf_version=$(fzf --version 2>/dev/null | awk '{print $1}')
17+
if [[ -z "$installed_fzf_version" ]]; then
18+
echo "fzf is not installed. Please install fzf first."
19+
exit 1
20+
fi
21+
22+
# Check fzf version
23+
required_fzf_version="0.49.0"
24+
higher_fzf_version=$(printf '%s\n' "$required_fzf_version" "$installed_fzf_version" | sort -V | tail -n1)
25+
if [[ "$higher_fzf_version" != "$installed_fzf_version" ]]; then
26+
echo "fzf version $required_fzf_version or higher is required. You have $installed_fzf_version."
27+
exit 1
28+
fi
29+
1530
# Set shell for fzf preview commands
1631
# Disable shellcheck for "which", because it suggests "command -v xxx" instead,
1732
# which is not a working replacement.
@@ -185,7 +200,7 @@ _forgit_log_enter() {
185200
local sha
186201
sha=$(echo "$1" | _forgit_extract_sha)
187202
shift
188-
echo "$sha" | xargs -I% "${FORGIT}" diff %^! "$@"
203+
echo "$sha" | xargs -I% "${FORGIT}" show % "$@"
189204
}
190205

191206
# git commit viewer
@@ -339,6 +354,83 @@ _forgit_diff() {
339354
return $fzf_exit_code
340355
}
341356

357+
_forgit_exec_show() {
358+
_forgit_show_git_opts=()
359+
_forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS"
360+
git show --pretty="" --diff-merges=first-parent --color=always "${_forgit_show_git_opts[@]}" "$@"
361+
}
362+
363+
_forgit_show_view() {
364+
local input_line=$1
365+
local diff_context=$2
366+
local commit=$3
367+
local repo
368+
repo=$(git rev-parse --show-toplevel)
369+
cd "$repo" || return 1
370+
echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \
371+
"$FORGIT" exec_show "${commit}^{commit}" -U"$diff_context" -- | _forgit_pager diff
372+
}
373+
374+
_forgit_show_preview() {
375+
local input_line=$1
376+
local diff_context=$2
377+
local commit=$3
378+
if [[ "$FZF_PREVIEW_LABEL" =~ "Diff" ]]; then
379+
_forgit_show_view "${input_line}" "${diff_context}" "${commit}"
380+
else
381+
git show --quiet --color=always "${FZF_PROMPT%% *}"
382+
fi
383+
}
384+
385+
_forgit_show_enter() {
386+
file=$1
387+
commit=$2
388+
_forgit_show_view "$file" "$_forgit_fullscreen_context" "${commit}"
389+
}
390+
391+
# git show viewer
392+
_forgit_show() {
393+
_forgit_inside_work_tree || return 1
394+
local files opts commit escaped_commit
395+
files=()
396+
if [[ $# -ne 0 ]]; then
397+
if git rev-parse "$1" -- &>/dev/null ; then
398+
commit="$1" && files=("${@:2}")
399+
else
400+
commit="HEAD" && files=("$@")
401+
fi
402+
else
403+
commit="HEAD"
404+
fi
405+
# Escape opening brackets to support stashes (see comment in _forgit_diff)
406+
escaped_commit=${commit//\{/\\\\\{}
407+
opts="
408+
$FORGIT_FZF_DEFAULT_OPTS
409+
+m -0 --bind=\"enter:execute($FORGIT show_enter {} $escaped_commit | $FORGIT pager enter)\"
410+
--preview=\"$FORGIT show_preview {} '$_forgit_preview_context' $escaped_commit\"
411+
--preview-label=\" Diff \"
412+
--bind=\"alt-e:execute($FORGIT edit_diffed_file {})+refresh-preview\"
413+
--bind=\"alt-t:transform:[[ ! \\\"\$FZF_PREVIEW_LABEL\\\" =~ 'Diff' ]] &&
414+
echo 'change-preview-label( Diff )+refresh-preview' ||
415+
echo 'change-preview-label( Commit Message )+refresh-preview'\"
416+
$FORGIT_DIFF_FZF_OPTS
417+
--prompt=\"${commit} > \"
418+
"
419+
_forgit_show_git_opts=()
420+
_forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS"
421+
# Add "^{commit}" suffix after the actual commit. This suppresses the tag information in case it is a tag.
422+
# See: https://git-scm.com/docs/git-show#Documentation/git-show.txt-codegitshow-s--formatsv100commitcode
423+
git show --pretty="" --name-status --diff-merges=first-parent "${_forgit_show_git_opts[@]}" "${commit}^{commit}" \
424+
-- "${files[@]}" |
425+
sed -E 's/^([[:alnum:]]+)[[:space:]]+(.*)$/[\1] \2/' |
426+
sed 's/ / -> /2' | expand -t 8 |
427+
FZF_DEFAULT_OPTS="$opts" fzf
428+
fzf_exit_code=$?
429+
# exit successfully on 130 (ctrl-c/esc)
430+
[[ $fzf_exit_code == 130 ]] && return 0
431+
return $fzf_exit_code
432+
}
433+
342434
_forgit_add_preview() {
343435
file=$(echo "$1" | _forgit_get_single_file_from_add_line)
344436
if (git status -s -- "$file" | grep '^??') &>/dev/null; then # diff with /dev/null for untracked files
@@ -1021,6 +1113,7 @@ public_commands=(
10211113
"rebase"
10221114
"reset_head"
10231115
"revert_commit"
1116+
"show"
10241117
"stash_show"
10251118
"stash_push"
10261119
)
@@ -1035,10 +1128,13 @@ private_commands=(
10351128
"cherry_pick_preview"
10361129
"clean_preview"
10371130
"diff_enter"
1131+
"exec_show"
10381132
"file_preview"
10391133
"ignore_preview"
10401134
"revert_preview"
10411135
"reset_head_preview"
1136+
"show_enter"
1137+
"show_preview"
10421138
"stash_push_preview"
10431139
"stash_show_preview"
10441140
"yank_sha"

completions/_git-forgit

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ _git-forgit() {
9797
reset_head) _git-staged ;;
9898
revert_commit) __git_recent_commits ;;
9999
stash_show) _git-stash-show ;;
100+
show) _git-show ;;
100101
esac
101102
}
102103

@@ -122,6 +123,7 @@ compdef _git-rebase forgit::rebase
122123
compdef _git-staged forgit::reset::head
123124
compdef __git_recent_commits forgit::revert::commit
124125
compdef _git-stash-show forgit::stash::show
126+
compdef _git-show forgit::show
125127

126128
# this is the case of calling the command and pressing tab
127129
# the very first time of a shell session, we have to manually

completions/git-forgit.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ _git_forgit()
7575
rebase
7676
reset_head
7777
revert_commit
78+
show
7879
stash_show
7980
stash_push
8081
"
@@ -101,6 +102,7 @@ _git_forgit()
101102
rebase) _git_rebase ;;
102103
reset_head) _git_reset ;;
103104
revert_commit) _git_revert ;;
105+
show) _git_show ;;
104106
stash_show) _git_stash_show ;;
105107
esac
106108
;;
@@ -135,6 +137,7 @@ then
135137
__git_complete forgit::rebase _git_rebase
136138
__git_complete forgit::reset::head _git_reset
137139
__git_complete forgit::revert::commit _git_revert
140+
__git_complete forgit::show _git_show
138141
__git_complete forgit::stash::show _git_stash_show
139142

140143
# Completion for forgit plugin shell aliases
@@ -154,6 +157,7 @@ then
154157
__git_complete "${forgit_rebase}" _git_rebase
155158
__git_complete "${forgit_reset_head}" _git_reset
156159
__git_complete "${forgit_revert_commit}" _git_revert
160+
__git_complete "${forgit_show}" _git_show
157161
__git_complete "${forgit_stash_show}" _git_stash_show
158162
fi
159163
fi

completions/git-forgit.fish

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a reflog -d 'git reflo
4040
complete -c git-forgit -n __fish_forgit_needs_subcommand -a rebase -d 'git rebase'
4141
complete -c git-forgit -n __fish_forgit_needs_subcommand -a reset_head -d 'git reset HEAD (unstage) selector'
4242
complete -c git-forgit -n __fish_forgit_needs_subcommand -a revert_commit -d 'git revert commit selector'
43+
complete -c git-forgit -n __fish_forgit_needs_subcommand -a show -d 'git show viewer'
4344
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git stash viewer'
4445
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_push -d 'git stash push selector'
4546

conf.d/forgit.plugin.fish

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ if test -z "$FORGIT_NO_ALIASES"
3737
abbr -a -- (string collect $forgit_log; or string collect "glo") git-forgit log
3838
abbr -a -- (string collect $forgit_reflog; or string collect "grl") git-forgit reflog
3939
abbr -a -- (string collect $forgit_diff; or string collect "gd") git-forgit diff
40+
abbr -a -- (string collect $forgit_show; or string collect "gso") git-forgit show
4041
abbr -a -- (string collect $forgit_ignore; or string collect "gi") git-forgit ignore
4142
abbr -a -- (string collect $forgit_checkout_file; or string collect "gcf") git-forgit checkout_file
4243
abbr -a -- (string collect $forgit_checkout_branch; or string collect "gcb") git-forgit checkout_branch

forgit.plugin.zsh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ forgit::diff() {
5353
"$FORGIT" diff "$@"
5454
}
5555

56+
forgit::show() {
57+
"$FORGIT" show "$@"
58+
}
59+
5660
forgit::add() {
5761
"$FORGIT" add "$@"
5862
}
@@ -146,6 +150,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
146150
export forgit_log="${forgit_log:-glo}"
147151
export forgit_reflog="${forgit_reflog:-grl}"
148152
export forgit_diff="${forgit_diff:-gd}"
153+
export forgit_show="${forgit_show:-gso}"
149154
export forgit_ignore="${forgit_ignore:-gi}"
150155
export forgit_checkout_file="${forgit_checkout_file:-gcf}"
151156
export forgit_checkout_branch="${forgit_checkout_branch:-gcb}"
@@ -166,6 +171,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
166171
alias "${forgit_log}"='forgit::log'
167172
alias "${forgit_reflog}"='forgit::reflog'
168173
alias "${forgit_diff}"='forgit::diff'
174+
alias "${forgit_show}"='forgit::show'
169175
alias "${forgit_ignore}"='forgit::ignore'
170176
alias "${forgit_checkout_file}"='forgit::checkout::file'
171177
alias "${forgit_checkout_branch}"='forgit::checkout::branch'

0 commit comments

Comments
 (0)