Skip to content

Add interactive git restore command#489

Open
jetm wants to merge 2 commits intowfxr:mainfrom
jetm:forgit-discard
Open

Add interactive git restore command#489
jetm wants to merge 2 commits intowfxr:mainfrom
jetm:forgit-discard

Conversation

@jetm
Copy link

@jetm jetm commented Feb 2, 2026

Summary

Add an interactive git restore command (grs) that provides an fzf-based
selector for discarding unstaged changes to working tree files. Closes #447.

Problem

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.

Changes

  • Add _forgit_restore function with fzf-based file selector and diff preview
  • Expose as grs alias, consistent with existing forgit commands
  • Integrate with shell completion systems across bash/zsh/fish
  • Add comprehensive test coverage

Check list

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have added unit tests for my code
  • I have made corresponding changes to the documentation

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Test
  • Documentation change

Test environment

  • Shell
    • bash
    • zsh
    • fish
  • OS
    • Linux
    • Mac OS X
    • Windows
    • Others:

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new interactive forgit_discard command (aliased as gdc) that combines unstaging and reverting uncommitted changes into a single operation. This fills a gap in forgit's workflow by providing a safe, interactive way to discard staged changes with preview and optional confirmation, consistent with forgit's design philosophy of making destructive operations more deliberate.

Changes:

  • Added _forgit_discard() function with preview and interactive file selection for discarding staged changes
  • Integrated the new command across all shell plugins (bash, zsh, fish) and completion systems
  • Added comprehensive test coverage for the new functionality
  • Updated documentation with usage examples and configuration options

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
bin/git-forgit Implements the core discard functionality with _forgit_discard(), _forgit_git_discard(), and _forgit_discard_preview() functions
tests/discard.test.sh Adds test suite covering empty state, tracked file restoration, new file handling, and unstaging behavior
forgit.plugin.zsh Registers forgit::discard() function and gdc alias for zsh
conf.d/forgit.plugin.fish Registers abbreviation for fish shell
completions/git-forgit.fish Adds completion support for fish shell
completions/git-forgit.bash Adds completion support for bash
completions/_git-forgit Adds completion support for zsh
README.md Documents the new command in features list and configuration tables
LICENSE Updates copyright year to 2026

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jetm jetm force-pushed the forgit-discard branch 2 times, most recently from ea508d8 to 7f90257 Compare February 11, 2026 23:12
@jetm jetm changed the title Add interactive git discard command Add interactive git restore and git discard commands Feb 11, 2026
@jetm jetm force-pushed the forgit-discard branch 2 times, most recently from 6d49fd7 to 6cfc362 Compare February 11, 2026 23:16
@jetm jetm force-pushed the forgit-discard branch from 6cfc362 to 55d5496 Compare March 6, 2026 22:38
jetm added 2 commits March 6, 2026 16:43
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 <floss@jetm.me>
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 <floss@jetm.me>
@jetm jetm force-pushed the forgit-discard branch from 55d5496 to 21d532c Compare March 6, 2026 22:43
@jetm jetm changed the title Add interactive git restore and git discard commands Add interactive git restore command Mar 6, 2026
Copy link
Collaborator

@sandr01d sandr01d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update @jetm! Looks mostly good, but needs a few changes here and there. The pipeline is failing because we require the usage of conventional commits. I'll do a full functional review, once the code is finalized.

Comment on lines -10 to -11
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There have been some commands removed here, probably by mistake.

Comment on lines +59 to +61
# for antidote (in .zsh_plugins.txt)
wfxr/forgit kind:defer

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change does not belong in this PR. Feel free to open a separate one.

Comment on lines +151 to +153



Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty lines should not be here.

Comment on lines +42 to +44
echo "staged change" >mixed.txt
git add mixed.txt
echo "unstaged change" >mixed.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need double >> here to not overwrite the file.

Suggested change
echo "staged change" >mixed.txt
git add mixed.txt
echo "unstaged change" >mixed.txt
echo "staged change" >> mixed.txt
git add mixed.txt
echo "unstaged change" >> mixed.txt

Comment on lines +49 to +58
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)"
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you commit the file after renaming, the renaming does not have any effect anymore in this case.

Comment on lines +60 to +67
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)"
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test has nothing to do with what it's name implies.

reflog) _git-forgit-reflog ;;
rebase) _git-rebase ;;
reset_head) _git-staged ;;
restore) _git-checkout-file ;;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Git has completions for git restore (_git-restore for zsh and _git_restore for bash). Any reason not to use those?

}

# git restore selector
_forgit_restore() {
Copy link
Collaborator

@sandr01d sandr01d Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One very neat thing about git restore IMO is that it allows to not only discard modified files, but also allows unstaging files with --staged and discarding from the working tree and the index (staged files) with --staged --worktree. I think it would be worth adding support for this, which would require us to do a bit of argument parsing to determine which files to show in the selection:

  • no --staged -> modified
  • --staged -> staged files
  • --staged --worktree -> modified and staged files

Would you be willing to implement this with this PR @jetm?

Comment on lines +11 to +13
echo "initial" >README.md
git add README.md
git commit -q -m "Initial commit"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is added and committed here, but not actually used in any of the tests, so this section should be removed.

}

function test_restore_reverts_modified_file_to_committed_state() {
echo "original content" >tracked.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor nitpick, but could you please add an extra space after the redirects? This applies to all redirects in this file.

Suggested change
echo "original content" >tracked.txt
echo "original content" > tracked.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for git restore

4 participants