Skip to content

feat: discard changes per file, hunk or line, after confirming - #12

Merged
Lars- merged 2 commits into
mainfrom
feat/discard
Nov 1, 2026
Merged

Lars- merged 2 commits into
mainfrom
feat/discard

Conversation

@Lars-

@Lars- Lars- commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

What this changes

You can now throw away changes you do not want, at three levels:

  • A file: every unstaged file has a Discard button next to Stage. For a tracked file it puts the file back the way the index has it, so staged changes stay. For an untracked file it deletes the file. Conflicted files have no Discard button; they are for the conflicts work on the roadmap.
  • A hunk: in the diff of an unstaged file, each hunk has Discard hunk next to Stage hunk.
  • Single lines: pick lines in the diff as for staging, then Discard N lines. A discarded addition goes, a discarded removal comes back.

Since none of this can be undone, each discard first asks, naming what will be lost ("Discard the unstaged changes to file.txt?", "Delete notes.tmp? Git does not track it.", "Discard this hunk of list.txt?"). The confirm button stays disabled while git is busy, so a click is never dropped.

Smaller things that came with it:

  • A diff of a working-tree file closes by itself once the file has no changes left or is gone, instead of showing an error for a deleted untracked file.
  • Dialogs are drawn before the panels, so Escape closes the dialog and no longer also closes the diff behind it (this also fixes the branch name prompt).
  • "Stage 1 lines" now reads "Stage 1 line".
  • Lines you picked in a diff stay picked when a reload leaves that diff unchanged. Since live refresh, any change elsewhere in the working tree reloads the tab, and that used to clear the pick between picking and clicking.

In the core this is one new Op::DiscardLines, which runs git apply --reverse on the working tree with the patch FileDiff::patch already builds for unstaging. The dialog (Confirm) is generic, so the next roadmap item (confirming a hard reset, deleting an unmerged branch, force push) can reuse it.

How it is tested

  • crates/core/tests/repo.rs, discard_single_lines_from_the_working_tree: discards a removed line, an added line and an added last line without a newline from a real repository, and checks that a staged change in the same file stays staged.

  • crates/app/src/tests.rs, through the accessibility tree:

    • discard_a_changed_file_once_confirmed: Cancel keeps the change, Discard restores the file.
    • discarding_an_open_untracked_file_deletes_it_and_closes_it: the file is deleted and its diff closes without an error.
    • discard_single_lines_and_hunks: discards one picked line, then checks Escape only closes the question, then discards the hunk.
    • The same test clicks Refresh between picking a line and discarding it, so the pick has to survive a reload.
  • Each new app test fails without the change it covers (checked by taking the change out).

  • The app tests now run their repositories with core.autocrlf=false, like the core tests, and settle also waits for a reload the watcher starts in the meantime (macOS reports changes late). The first CI run failed on both.

  • Tried by hand in the release build on Linux: deleting an untracked file, discarding a removed line, a hunk and a whole open file, and Escape in the dialog.

  • cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings and cargo test --workspace pass

  • It adds as little code as the change needs, and removes what it makes unnecessary

  • Nothing in it comes from GitKraken or another closed-source client (see CONTRIBUTING.md)

@Lars-
Lars- merged commit 54e52b4 into main Nov 1, 2026
7 checks passed
@Lars-
Lars- deleted the feat/discard branch November 1, 2026 10:23
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.

1 participant