From faf1ebff60a8dae0381746b98a04c3b6f572598f Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Wed, 11 Feb 2026 16:57:08 -0600 Subject: [PATCH 1/2] forgit: Add interactive restore command for discarding changes Currently there is no interactive way to selectively discard modifications to working tree files. Users must either manually type file paths with git restore or use git checkout, which is the older discouraged syntax. This makes it cumbersome to selectively discard changes when reviewing multiple modified files. Add _forgit_restore function that provides an fzf-based selector for modified files with diff preview. This follows the same pattern as existing forgit commands like reset_head and checkout_file, allowing users to interactively review diffs before discarding changes. The command is exposed as 'grs' alias and integrated with shell completion systems across bash/zsh/fish. Signed-off-by: Javier Tia --- ...-dedicated-preview-pager-configurati.patch | 189 ++++++++++++++++++ README.md | 10 + bin/git-forgit | 35 ++++ completions/_git-forgit | 3 + completions/git-forgit.bash | 4 + completions/git-forgit.fish | 5 +- conf.d/forgit.plugin.fish | 1 + forgit.plugin.zsh | 6 + 8 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 0001-forgit-Introduce-dedicated-preview-pager-configurati.patch diff --git a/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch b/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch new file mode 100644 index 00000000..d2fbbdff --- /dev/null +++ b/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch @@ -0,0 +1,189 @@ +From a3b4346b1de05957e96f67e2187289c8588763bc Mon Sep 17 00:00:00 2001 +From: Javier Tia +Date: Fri, 6 Mar 2026 16:24:07 -0600 +Subject: [PATCH] forgit: Introduce dedicated preview pager configuration + +Previously, all fzf preview panels reused the per-command pagers +(show, diff, blame) to render their output. This coupled the +interactive preview appearance to the full-screen pager behaviour, +making it impossible to independently control how diffs and commit +content are rendered inside the fzf preview window. + +Introduce a single `FORGIT_PREVIEW_PAGER` variable that governs all +fzf preview panels. When unset it auto-detects an appropriate +renderer: delta (with paging disabled so it fits inline), then bat, +then cat as a final fallback. This lets users configure a lightweight +renderer for previews while keeping a different pager for full-screen +output, and makes the auto-detection logic explicit and consistent +across every preview function. + +Signed-off-by: Javier Tia +--- + README.md | 1 + + bin/git-forgit | 38 ++++++++++++++++++++++---------------- + 2 files changed, 23 insertions(+), 16 deletions(-) + +diff --git a/README.md b/README.md +index 6f7bbba..e1d0644 100644 +--- a/README.md ++++ b/README.md +@@ -332,6 +332,7 @@ variables: + | pager on `git show` | `FORGIT_SHOW_PAGER` | `git config pager.show` _or_ `$FORGIT_PAGER` | + | pager on `git diff` | `FORGIT_DIFF_PAGER` | `git config pager.diff` _or_ `$FORGIT_PAGER` | + | pager on `git blame` | `FORGIT_BLAME_PAGER` | `git config pager.blame` _or_ `$FORGIT_PAGER` | ++| pager in fzf previews | `FORGIT_PREVIEW_PAGER` | `delta --paging=never` _or_ `bat --plain` _or_ `cat` | + | pager on `gitignore` | `FORGIT_IGNORE_PAGER` | `bat -l gitignore --color always` _or_ `cat` | + | pager on `gitatrributes` | `FORGIT_ATTRIBUTES_PAGER` | `bat -l gitattributes --color always` _or_ `cat` | + | git log format | `FORGIT_GLO_FORMAT` | `%C(auto)%h%d %s %C(black)%C(bold)%cr%reset` | +diff --git a/bin/git-forgit b/bin/git-forgit +index 76e2f20..671dbc1 100755 +--- a/bin/git-forgit ++++ b/bin/git-forgit +@@ -147,6 +147,12 @@ _forgit_get_pager() { + esac + } + ++_forgit_preview_pager() { ++ local pager ++ pager="${FORGIT_PREVIEW_PAGER:-$(hash delta &>/dev/null && echo 'delta --paging=never' || (hash bat &>/dev/null && echo 'bat --plain --color=always' || echo 'cat'))}" ++ eval "$pager" ++} ++ + _forgit_is_file_tracked() { + git ls-files "$1" --error-unmatch &> /dev/null + } +@@ -174,7 +180,7 @@ _forgit_log_preview() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) + shift +- git show --color=always -U"$_forgit_preview_context" "${sha}" -- "$@" | _forgit_pager show ++ git show --color=always -U"$_forgit_preview_context" "${sha}" -- "$@" | _forgit_preview_pager + } + + _forgit_log_enter() { +@@ -429,9 +435,9 @@ _forgit_add_preview() { + return 0 + fi + if (git status -s -- "$file" | grep '^??') &>/dev/null; then # diff with /dev/null for untracked files +- git diff --color=always --no-index -- /dev/null "$file" | _forgit_pager diff | sed '2 s/added:/untracked:/' ++ git diff --color=always --no-index -- /dev/null "$file" | _forgit_preview_pager | sed '2 s/added:/untracked:/' + else +- git diff --color=always -- "$file" | _forgit_pager diff ++ git diff --color=always -- "$file" | _forgit_preview_pager + fi + } + +@@ -487,7 +493,7 @@ _forgit_add() { + + _forgit_reset_head_preview() { + file=$1 +- git diff --staged --color=always -- "$file" | _forgit_pager diff ++ git diff --staged --color=always -- "$file" | _forgit_preview_pager + } + + _forgit_git_reset_head() { +@@ -523,7 +529,7 @@ _forgit_reset_head() { + } + + _forgit_restore_preview() { +- git diff --color=always -- "$1" | _forgit_pager diff ++ git diff --color=always -- "$1" | _forgit_preview_pager + } + + _forgit_git_restore() { +@@ -556,7 +562,7 @@ _forgit_restore() { + } + + _forgit_discard_preview() { +- git diff --color=always HEAD -- "$1" | _forgit_pager diff ++ git diff --color=always HEAD -- "$1" | _forgit_preview_pager + } + + _forgit_git_discard() { +@@ -593,7 +599,7 @@ _forgit_discard() { + _forgit_stash_show_preview() { + local stash + stash=$(echo "$1" | _forgit_extract_stash_name) +- git show --color=always -U"$_forgit_preview_context" "${stash}" | _forgit_pager show ++ git show --color=always -U"$_forgit_preview_context" "${stash}" | _forgit_preview_pager + } + + _forgit_stash_show_enter() { +@@ -625,9 +631,9 @@ _forgit_stash_show() { + + _forgit_stash_push_preview() { + if _forgit_is_file_tracked "$1"; then +- git diff --color=always -- "$1" | _forgit_pager diff ++ git diff --color=always -- "$1" | _forgit_preview_pager + else +- git diff --color=always /dev/null "$1" | _forgit_pager diff ++ git diff --color=always /dev/null "$1" | _forgit_preview_pager + fi + } + +@@ -678,7 +684,7 @@ _forgit_clean_preview() { + if [[ -d "$path" ]]; then + eval "$_forgit_dir_view \"$path\"" + else +- git diff --color=always /dev/null "$path" | _forgit_pager diff ++ git diff --color=always /dev/null "$path" | _forgit_preview_pager + fi + } + +@@ -711,7 +717,7 @@ _forgit_clean() { + _forgit_cherry_pick_preview() { + local sha + sha=$(echo "$1" | cut -f2- | _forgit_extract_sha) +- git show --color=always "${sha}" | _forgit_pager show ++ git show --color=always "${sha}" | _forgit_preview_pager + } + + _forgit_cherry_pick() { +@@ -827,7 +833,7 @@ _forgit_file_preview() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) + shift +- git show --color=always "${sha}" -- "$@" | _forgit_pager show ++ git show --color=always "${sha}" -- "$@" | _forgit_preview_pager + } + + _forgit_fixup() { +@@ -902,7 +908,7 @@ _forgit_reword() { + } + + _forgit_checkout_file_preview() { +- git diff --color=always -- "$1" | _forgit_pager diff ++ git diff --color=always -- "$1" | _forgit_preview_pager + } + + _forgit_git_checkout_file() { +@@ -1050,7 +1056,7 @@ _forgit_checkout_tag() { + _forgit_checkout_commit_preview() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) +- git show --color=always "${sha}" | _forgit_pager show ++ git show --color=always "${sha}" | _forgit_preview_pager + } + + _forgit_git_checkout_commit() { +@@ -1115,7 +1121,7 @@ _forgit_branch_delete() { + _forgit_revert_preview() { + local sha + sha=$(echo "$1" | cut -f2- | _forgit_extract_sha) +- git show --color=always "${sha}" | _forgit_pager show ++ git show --color=always "${sha}" | _forgit_preview_pager + } + + _forgit_git_revert() { +@@ -1166,7 +1172,7 @@ _forgit_blame_preview() { + if _forgit_is_file_tracked "$1"; then + _forgit_blame_git_opts=() + _forgit_parse_array _forgit_blame_git_opts "$FORGIT_BLAME_GIT_OPTS" +- git blame --date=short "${_forgit_blame_git_opts[@]}" "$@" | _forgit_pager blame ++ git blame --date=short "${_forgit_blame_git_opts[@]}" "$@" | _forgit_preview_pager + else + echo "File not tracked" + fi +-- +2.53.0 + diff --git a/README.md b/README.md index f888f0b4..26b84a08 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ omf install https://github.com/wfxr/forgit # for zinit zinit load wfxr/forgit +# for antidote (in .zsh_plugins.txt) +wfxr/forgit kind:defer + # for oh-my-zsh git clone https://github.com/wfxr/forgit.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/forgit @@ -144,6 +147,10 @@ If you're having issues after updating, and commands such as `forgit::add` or al - **Interactive `git reset HEAD ` selector** (`grh`) +- **Interactive `git restore ` selector** (`grs`) + + + - **Interactive `git checkout ` selector** (`gcf`) - **Interactive `git checkout ` selector** (`gcb`) @@ -222,6 +229,7 @@ forgit_diff=gd forgit_show=gso forgit_add=ga forgit_reset_head=grh +forgit_restore=grs forgit_ignore=gi forgit_attributes=gat forgit_checkout_file=gcf @@ -286,6 +294,7 @@ These are passed to the according `git` calls. | `gd` | `FORGIT_DIFF_GIT_OPTS` | | `gso` | `FORGIT_SHOW_GIT_OPTS` | | `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` | +| `grs` | `FORGIT_RESTORE_GIT_OPTS` | | `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` | | `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` | | `gsw` | `FORGIT_SWITCH_BRANCH_GIT_OPTS` | @@ -346,6 +355,7 @@ Customizing fzf options for each command individually is also supported: | `gd` | `FORGIT_DIFF_FZF_OPTS` | | `gso` | `FORGIT_SHOW_FZF_OPTS` | | `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` | +| `grs` | `FORGIT_RESTORE_FZF_OPTS` | | `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` | | `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` | | `gsw` | `FORGIT_SWITCH_BRANCH_FZF_OPTS` | diff --git a/bin/git-forgit b/bin/git-forgit index f54845f2..071e8e77 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -522,6 +522,39 @@ _forgit_reset_head() { git status --short } +_forgit_restore_preview() { + git diff --color=always -- "$1" | _forgit_pager diff +} + +_forgit_git_restore() { + _forgit_restore_git_opts=() + _forgit_parse_array _forgit_restore_git_opts "$FORGIT_RESTORE_GIT_OPTS" + git restore "${_forgit_restore_git_opts[@]}" "$@" +} + +# git restore selector +_forgit_restore() { + _forgit_inside_work_tree || return 1 + local files opts + _forgit_contains_non_flags "$@" && { + _forgit_git_restore "$@" + return $? + } + [[ $(_forgit_list_files --modified | wc -l) -eq 0 ]] && echo 'Nothing to restore.' && return 1 + opts=" + $FORGIT_FZF_DEFAULT_OPTS + -m -0 + --preview=\"$FORGIT restore_preview {}\" + $FORGIT_RESTORE_FZF_OPTS + " + files=() + while IFS='' read -r file; do + files+=("$file") + done < <(_forgit_list_files --modified | + FZF_DEFAULT_OPTS="$opts" fzf) + [[ "${#files[@]}" -gt 0 ]] && _forgit_git_restore "$@" "${files[@]}" +} + _forgit_stash_show_preview() { local stash stash=$(echo "$1" | _forgit_extract_stash_name) @@ -1303,6 +1336,7 @@ PUBLIC_COMMANDS=( "reflog" "rebase" "reset_head" + "restore" "revert_commit" "show" "stash_show" @@ -1324,6 +1358,7 @@ PRIVATE_COMMANDS=( "path_preview" "revert_preview" "reset_head_preview" + "restore_preview" "show_enter" "show_preview" "stash_push_preview" diff --git a/completions/_git-forgit b/completions/_git-forgit index ee80c88b..00652c49 100644 --- a/completions/_git-forgit +++ b/completions/_git-forgit @@ -74,6 +74,7 @@ _git-forgit() { 'reflog:git reflog viewer' 'rebase:git rebase' 'reset_head:git reset HEAD (unstage) selector' + 'restore:git restore file selector' 'revert_commit:git revert commit selector' 'reword:git fixup=reword' 'squash:git squash' @@ -97,6 +98,7 @@ _git-forgit() { reflog) _git-forgit-reflog ;; rebase) _git-rebase ;; reset_head) _git-staged ;; + restore) _git-checkout-file ;; revert_commit) __git_recent_commits ;; reword) _git-log ;; squash) _git-log ;; @@ -126,6 +128,7 @@ compdef _git-log forgit::log compdef _git-reflog forgit::reflog compdef _git-rebase forgit::rebase compdef _git-staged forgit::reset::head +compdef _git-checkout-file forgit::restore compdef __git_recent_commits forgit::revert::commit compdef _git-log forgit::reword compdef _git-log forgit::squash diff --git a/completions/git-forgit.bash b/completions/git-forgit.bash index 7a508ce7..5dcf0e55 100755 --- a/completions/git-forgit.bash +++ b/completions/git-forgit.bash @@ -74,6 +74,7 @@ _git_forgit() reflog rebase reset_head + restore revert_commit reword show @@ -103,6 +104,7 @@ _git_forgit() reflog) _git_reflog ;; rebase) _git_rebase ;; reset_head) _git_reset ;; + restore) _git_checkout_file ;; revert_commit) _git_revert ;; reword) _git_log ;; show) _git_show ;; @@ -141,6 +143,7 @@ then __git_complete forgit::reflog _git_reflog __git_complete forgit::rebase _git_rebase __git_complete forgit::reset::head _git_reset + __git_complete forgit::restore _git_checkout_file __git_complete forgit::revert::commit _git_revert __git_complete forgit::reword _git_log __git_complete forgit::show _git_show @@ -164,6 +167,7 @@ then __git_complete "${forgit_reflog}" _git_reflog __git_complete "${forgit_rebase}" _git_rebase __git_complete "${forgit_reset_head}" _git_reset + __git_complete "${forgit_restore}" _git_checkout_file __git_complete "${forgit_revert_commit}" _git_revert __git_complete "${forgit_reword}" _git_log __git_complete "${forgit_show}" _git_show diff --git a/completions/git-forgit.fish b/completions/git-forgit.fish index 48f995cf..9d5a9dac 100644 --- a/completions/git-forgit.fish +++ b/completions/git-forgit.fish @@ -7,8 +7,7 @@ function __fish_forgit_needs_subcommand for subcmd in add blame branch_delete checkout_branch checkout_commit checkout_file checkout_tag \ - cherry_pick cherry_pick_from_branch clean diff fixup ignore log reflog rebase reset_head \ - revert_commit reword squash stash_show stash_push switch_branch + restore revert_commit reword squash stash_show stash_push switch_branch if contains -- $subcmd (commandline -opc) return 1 end @@ -39,6 +38,7 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a log -d 'git commit v complete -c git-forgit -n __fish_forgit_needs_subcommand -a reflog -d 'git reflog viewer' 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 restore -d 'git restore file 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 reword -d 'git fixup=reword' complete -c git-forgit -n __fish_forgit_needs_subcommand -a show -d 'git show viewer' @@ -61,6 +61,7 @@ complete -c git-forgit -n '__fish_seen_subcommand_from log' -a "(complete -C 'gi complete -c git-forgit -n '__fish_seen_subcommand_from reflog' -a "(complete -C 'git reflog ')" complete -c git-forgit -n '__fish_seen_subcommand_from rebase' -a "(complete -C 'git rebase ')" complete -c git-forgit -n '__fish_seen_subcommand_from reset_head' -a "(__fish_git_files all-staged)" +complete -c git-forgit -n '__fish_seen_subcommand_from restore' -a "(__fish_git_files modified)" complete -c git-forgit -n '__fish_seen_subcommand_from revert_commit' -a "(__fish_git_commits)" complete -c git-forgit -n '__fish_seen_subcommand_from reword' -a "(complete -C 'git log ')" complete -c git-forgit -n '__fish_seen_subcommand_from show' -a "(complete -C 'git show ')" diff --git a/conf.d/forgit.plugin.fish b/conf.d/forgit.plugin.fish index 2ffd4299..5d5aefda 100644 --- a/conf.d/forgit.plugin.fish +++ b/conf.d/forgit.plugin.fish @@ -34,6 +34,7 @@ alias git-forgit "$FORGIT" if test -z "$FORGIT_NO_ALIASES" abbr -a -- (string collect $forgit_add; or string collect "ga") git-forgit add abbr -a -- (string collect $forgit_reset_head; or string collect "grh") git-forgit reset_head + abbr -a -- (string collect $forgit_restore; or string collect "grs") git-forgit restore 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 diff --git a/forgit.plugin.zsh b/forgit.plugin.zsh index 012c354f..a649d92a 100755 --- a/forgit.plugin.zsh +++ b/forgit.plugin.zsh @@ -68,6 +68,10 @@ forgit::reset::head() { "$FORGIT" reset_head "$@" } +forgit::restore() { + "$FORGIT" restore "$@" +} + forgit::stash::show() { "$FORGIT" stash_show "$@" } @@ -166,6 +170,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then builtin export forgit_add="${forgit_add:-ga}" builtin export forgit_reset_head="${forgit_reset_head:-grh}" + builtin export forgit_restore="${forgit_restore:-grs}" builtin export forgit_log="${forgit_log:-glo}" builtin export forgit_reflog="${forgit_reflog:-grl}" builtin export forgit_diff="${forgit_diff:-gd}" @@ -191,6 +196,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then builtin alias "${forgit_add}"='forgit::add' builtin alias "${forgit_reset_head}"='forgit::reset::head' + builtin alias "${forgit_restore}"='forgit::restore' builtin alias "${forgit_log}"='forgit::log' builtin alias "${forgit_reflog}"='forgit::reflog' builtin alias "${forgit_diff}"='forgit::diff' From 21d532c851e902021a57d4d902009e3f4047a51b Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Wed, 11 Feb 2026 16:57:27 -0600 Subject: [PATCH 2/2] tests: Add comprehensive test coverage for git restore functionality Currently the restore functionality lacks automated tests, making it difficult to verify correctness during refactoring and increasing the risk of regressions. Without tests, developers cannot confidently modify the restore behavior or ensure edge cases are handled properly. Add a complete test suite covering core restore scenarios including modified files, untracked files, mixed staged/unstaged changes, and renamed files. These tests validate that restore correctly reverts working tree changes while preserving staged content and ignoring untracked files, ensuring the command behaves consistently with Git's restore semantics. Signed-off-by: Javier Tia --- ...-dedicated-preview-pager-configurati.patch | 189 ------------------ tests/restore.test.sh | 67 +++++++ 2 files changed, 67 insertions(+), 189 deletions(-) delete mode 100644 0001-forgit-Introduce-dedicated-preview-pager-configurati.patch create mode 100644 tests/restore.test.sh diff --git a/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch b/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch deleted file mode 100644 index d2fbbdff..00000000 --- a/0001-forgit-Introduce-dedicated-preview-pager-configurati.patch +++ /dev/null @@ -1,189 +0,0 @@ -From a3b4346b1de05957e96f67e2187289c8588763bc Mon Sep 17 00:00:00 2001 -From: Javier Tia -Date: Fri, 6 Mar 2026 16:24:07 -0600 -Subject: [PATCH] forgit: Introduce dedicated preview pager configuration - -Previously, all fzf preview panels reused the per-command pagers -(show, diff, blame) to render their output. This coupled the -interactive preview appearance to the full-screen pager behaviour, -making it impossible to independently control how diffs and commit -content are rendered inside the fzf preview window. - -Introduce a single `FORGIT_PREVIEW_PAGER` variable that governs all -fzf preview panels. When unset it auto-detects an appropriate -renderer: delta (with paging disabled so it fits inline), then bat, -then cat as a final fallback. This lets users configure a lightweight -renderer for previews while keeping a different pager for full-screen -output, and makes the auto-detection logic explicit and consistent -across every preview function. - -Signed-off-by: Javier Tia ---- - README.md | 1 + - bin/git-forgit | 38 ++++++++++++++++++++++---------------- - 2 files changed, 23 insertions(+), 16 deletions(-) - -diff --git a/README.md b/README.md -index 6f7bbba..e1d0644 100644 ---- a/README.md -+++ b/README.md -@@ -332,6 +332,7 @@ variables: - | pager on `git show` | `FORGIT_SHOW_PAGER` | `git config pager.show` _or_ `$FORGIT_PAGER` | - | pager on `git diff` | `FORGIT_DIFF_PAGER` | `git config pager.diff` _or_ `$FORGIT_PAGER` | - | pager on `git blame` | `FORGIT_BLAME_PAGER` | `git config pager.blame` _or_ `$FORGIT_PAGER` | -+| pager in fzf previews | `FORGIT_PREVIEW_PAGER` | `delta --paging=never` _or_ `bat --plain` _or_ `cat` | - | pager on `gitignore` | `FORGIT_IGNORE_PAGER` | `bat -l gitignore --color always` _or_ `cat` | - | pager on `gitatrributes` | `FORGIT_ATTRIBUTES_PAGER` | `bat -l gitattributes --color always` _or_ `cat` | - | git log format | `FORGIT_GLO_FORMAT` | `%C(auto)%h%d %s %C(black)%C(bold)%cr%reset` | -diff --git a/bin/git-forgit b/bin/git-forgit -index 76e2f20..671dbc1 100755 ---- a/bin/git-forgit -+++ b/bin/git-forgit -@@ -147,6 +147,12 @@ _forgit_get_pager() { - esac - } - -+_forgit_preview_pager() { -+ local pager -+ pager="${FORGIT_PREVIEW_PAGER:-$(hash delta &>/dev/null && echo 'delta --paging=never' || (hash bat &>/dev/null && echo 'bat --plain --color=always' || echo 'cat'))}" -+ eval "$pager" -+} -+ - _forgit_is_file_tracked() { - git ls-files "$1" --error-unmatch &> /dev/null - } -@@ -174,7 +180,7 @@ _forgit_log_preview() { - local sha - sha=$(echo "$1" | _forgit_extract_sha) - shift -- git show --color=always -U"$_forgit_preview_context" "${sha}" -- "$@" | _forgit_pager show -+ git show --color=always -U"$_forgit_preview_context" "${sha}" -- "$@" | _forgit_preview_pager - } - - _forgit_log_enter() { -@@ -429,9 +435,9 @@ _forgit_add_preview() { - return 0 - fi - if (git status -s -- "$file" | grep '^??') &>/dev/null; then # diff with /dev/null for untracked files -- git diff --color=always --no-index -- /dev/null "$file" | _forgit_pager diff | sed '2 s/added:/untracked:/' -+ git diff --color=always --no-index -- /dev/null "$file" | _forgit_preview_pager | sed '2 s/added:/untracked:/' - else -- git diff --color=always -- "$file" | _forgit_pager diff -+ git diff --color=always -- "$file" | _forgit_preview_pager - fi - } - -@@ -487,7 +493,7 @@ _forgit_add() { - - _forgit_reset_head_preview() { - file=$1 -- git diff --staged --color=always -- "$file" | _forgit_pager diff -+ git diff --staged --color=always -- "$file" | _forgit_preview_pager - } - - _forgit_git_reset_head() { -@@ -523,7 +529,7 @@ _forgit_reset_head() { - } - - _forgit_restore_preview() { -- git diff --color=always -- "$1" | _forgit_pager diff -+ git diff --color=always -- "$1" | _forgit_preview_pager - } - - _forgit_git_restore() { -@@ -556,7 +562,7 @@ _forgit_restore() { - } - - _forgit_discard_preview() { -- git diff --color=always HEAD -- "$1" | _forgit_pager diff -+ git diff --color=always HEAD -- "$1" | _forgit_preview_pager - } - - _forgit_git_discard() { -@@ -593,7 +599,7 @@ _forgit_discard() { - _forgit_stash_show_preview() { - local stash - stash=$(echo "$1" | _forgit_extract_stash_name) -- git show --color=always -U"$_forgit_preview_context" "${stash}" | _forgit_pager show -+ git show --color=always -U"$_forgit_preview_context" "${stash}" | _forgit_preview_pager - } - - _forgit_stash_show_enter() { -@@ -625,9 +631,9 @@ _forgit_stash_show() { - - _forgit_stash_push_preview() { - if _forgit_is_file_tracked "$1"; then -- git diff --color=always -- "$1" | _forgit_pager diff -+ git diff --color=always -- "$1" | _forgit_preview_pager - else -- git diff --color=always /dev/null "$1" | _forgit_pager diff -+ git diff --color=always /dev/null "$1" | _forgit_preview_pager - fi - } - -@@ -678,7 +684,7 @@ _forgit_clean_preview() { - if [[ -d "$path" ]]; then - eval "$_forgit_dir_view \"$path\"" - else -- git diff --color=always /dev/null "$path" | _forgit_pager diff -+ git diff --color=always /dev/null "$path" | _forgit_preview_pager - fi - } - -@@ -711,7 +717,7 @@ _forgit_clean() { - _forgit_cherry_pick_preview() { - local sha - sha=$(echo "$1" | cut -f2- | _forgit_extract_sha) -- git show --color=always "${sha}" | _forgit_pager show -+ git show --color=always "${sha}" | _forgit_preview_pager - } - - _forgit_cherry_pick() { -@@ -827,7 +833,7 @@ _forgit_file_preview() { - local sha - sha=$(echo "$1" | _forgit_extract_sha) - shift -- git show --color=always "${sha}" -- "$@" | _forgit_pager show -+ git show --color=always "${sha}" -- "$@" | _forgit_preview_pager - } - - _forgit_fixup() { -@@ -902,7 +908,7 @@ _forgit_reword() { - } - - _forgit_checkout_file_preview() { -- git diff --color=always -- "$1" | _forgit_pager diff -+ git diff --color=always -- "$1" | _forgit_preview_pager - } - - _forgit_git_checkout_file() { -@@ -1050,7 +1056,7 @@ _forgit_checkout_tag() { - _forgit_checkout_commit_preview() { - local sha - sha=$(echo "$1" | _forgit_extract_sha) -- git show --color=always "${sha}" | _forgit_pager show -+ git show --color=always "${sha}" | _forgit_preview_pager - } - - _forgit_git_checkout_commit() { -@@ -1115,7 +1121,7 @@ _forgit_branch_delete() { - _forgit_revert_preview() { - local sha - sha=$(echo "$1" | cut -f2- | _forgit_extract_sha) -- git show --color=always "${sha}" | _forgit_pager show -+ git show --color=always "${sha}" | _forgit_preview_pager - } - - _forgit_git_revert() { -@@ -1166,7 +1172,7 @@ _forgit_blame_preview() { - if _forgit_is_file_tracked "$1"; then - _forgit_blame_git_opts=() - _forgit_parse_array _forgit_blame_git_opts "$FORGIT_BLAME_GIT_OPTS" -- git blame --date=short "${_forgit_blame_git_opts[@]}" "$@" | _forgit_pager blame -+ git blame --date=short "${_forgit_blame_git_opts[@]}" "$@" | _forgit_preview_pager - else - echo "File not tracked" - fi --- -2.53.0 - diff --git a/tests/restore.test.sh b/tests/restore.test.sh new file mode 100644 index 00000000..69f45284 --- /dev/null +++ b/tests/restore.test.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +function set_up_before_script() { + source bin/git-forgit + export GIT_CONFIG_SYSTEM=/dev/null + export GIT_CONFIG_GLOBAL=/dev/null + cd "$(bashunit::temp_dir)" || return 1 + git init -q + git config user.email "test@example.com" + git config user.name "Test User" + echo "initial" >README.md + git add README.md + git commit -q -m "Initial commit" +} + +function test_restore_shows_message_when_no_modified_files() { + output=$(_forgit_restore 2>&1) + assert_general_error + assert_same "Nothing to restore." "$output" +} + +function test_restore_reverts_modified_file_to_committed_state() { + echo "original content" >tracked.txt + git add tracked.txt + git commit -q -m "Add tracked file" + echo "modified content" >tracked.txt + _forgit_git_restore tracked.txt + assert_same "original content" "$(cat tracked.txt)" +} + +function test_restore_does_not_affect_untracked_files() { + echo "untracked content" >untracked.txt + _forgit_git_restore untracked.txt 2>/dev/null || true + assert_file_exists untracked.txt + assert_same "untracked content" "$(cat untracked.txt)" +} + +function test_restore_only_reverts_unstaged_changes_when_file_has_staged_and_unstaged() { + echo "committed" >mixed.txt + git add mixed.txt + git commit -q -m "Add mixed file" + echo "staged change" >mixed.txt + git add mixed.txt + echo "unstaged change" >mixed.txt + _forgit_git_restore mixed.txt + assert_same "staged change" "$(cat mixed.txt)" +} + +function test_restore_with_renamed_file() { + echo "rename content" >before-rename.txt + git add before-rename.txt + git commit -q -m "Add file for rename test" + git mv before-rename.txt after-rename.txt + git commit -q -m "Rename file" + echo "modified after rename" >after-rename.txt + _forgit_git_restore after-rename.txt + assert_same "rename content" "$(cat after-rename.txt)" +} + +function test_restore_passes_through_arguments_when_non_flags_provided() { + echo "original" >passthrough.txt + git add passthrough.txt + git commit -q -m "Add passthrough file" + echo "modified" >passthrough.txt + _forgit_restore passthrough.txt + assert_same "original" "$(cat passthrough.txt)" +}