Added Tracked , Staged options to stash (#62254)

# Objective

Closes #62252 

The Git Panel could only stash *everything* — `Stash All` runs
`git stash push --include-untracked`, sweeping tracked edits and
untracked files
into a single entry. There was no way to stash a subset, so the common
workflows
of "park my tracked edits but keep my new scratch files" and "park what
I've
staged and keep working on the rest" required dropping to the terminal.

## Images

<img width="389" height="358" alt="Screenshot 2026-08-10 at 3 10 50 PM"
src="https://github.com/user-attachments/assets/18e4c943-e320-4802-ada8-59e54bf4cefd"
/>

<img width="504" height="462" alt="Screenshot 2026-08-10 at 3 10 37 PM"
src="https://github.com/user-attachments/assets/783237eb-980d-47bc-a0f5-17b03a23a60c"
/>






## Solution

Add two stash variants alongside `Stash All`, surfaced in the Git
Panel's
overflow menu based on how the list is currently grouped, so the menu
mirrors the
sections the user can actually see:

| Group By | Stash entries offered |
| --- | --- |
| None | Stash All |
| Tracked & Untracked | Stash All, **Stash Tracked** |
| Staged & Unstaged | Stash All, **Stash Staged** |

- **`git::StashTracked`** stashes tracked changes and leaves untracked
files in
place. It reuses the existing pathspec plumbing
(`Repository::stash_entries`),
  filtering the status list down to the paths to stash.
- **`git::StashStaged`** stashes the index only, leaving unstaged
changes in
place. This *cannot* be expressed as a pathspec — a partially staged
file would
have its unstaged hunks stashed too — so it needs git's own `--staged`
flag.
  That meant a new `GitRepository::stash_staged` backend method and an
`optional bool staged` field on `proto::Stash` so remote projects work
too.

Both actions are unbound by default and are dispatchable from the
command palette
when the panel is focused.

One subtlety worth calling out for review: `Stash Tracked` filters on
`FileStatus::is_created()`, not `is_untracked()`. Staging a new file
flips it from
`Untracked` to `Tracked { Added }`, but the panel still lists it under
**Untracked** — using `is_untracked()` meant staged-new files were
silently
stashed. `is_created()` is the same predicate the panel uses to build
that section
(`git_panel.rs`), so the menu item and the list can no longer disagree.

This branch also includes a separate commit adding **per-section
staging**
(`git::StageSection` / `git::UnstageSection`) — right-click a file to
stage or
unstage every entry in its section. Happy to split that into its own PR
if
preferred.

## Testing

Manually tested on macOS against a scratch repo with a mix of states:
modified
tracked files, untracked files, and untracked files that had been
staged.

- `Stash Tracked` with tracked edits + untracked files → only tracked
edits
  stashed; untracked files remain.
- `Stash Tracked` with untracked files **staged** → they remain, staged.
This was
  broken in an earlier revision and drove the `is_created()` fix above.
- `Stash Staged` with one file staged and another modified-but-unstaged
→ only the
  staged file is stashed; the unstaged edit and untracked files survive.
- `Stash Pop` round-trips both cases back to the original state, with no
conflicts.
- Menu contents and disabled states verified in all three Group By
modes.
- Per-section staging covered by a new unit test,
  `test_stage_section_scopes_to_selected_section`.

Not covered by automated tests: the stash actions themselves.
`FakeGitRepository`
leaves every stash method `unimplemented!()`, so stash behavior isn't
reachable
from GPUI tests today — consistent with the existing untested
`StashAll`. Adding
fake-repo stash support looks like a worthwhile follow-up but felt out
of scope here.

Reviewers on non-macOS platforms: nothing here is platform-specific.
Note that
`Stash Staged` requires **git 2.35+** (Jan 2022) for `git stash push
--staged`;
older git surfaces a clear error toast rather than failing opaquely. The
remote
path (`proto::Stash.staged`) has not been exercised against a live
collab session.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Added `Stash Tracked` and `Stash Staged` options to the Git Panel,
letting you stash only tracked changes or only staged changes.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
This commit is contained in:
PriyadharshanandChristopher Biscardi authored and GitHub committed 2026-08-18 05:36:16 +00:00
1 parent 52f9567fb4
commit f652da59e8
8 files changed
+520 -14

No files matched your search

+12 -2
View File
@@ -155,6 +155,8 @@ From the panel, you can simply type a commit message and hit the commit button,
Entries can be staged using each individual entry's checkbox. All changes can be staged using the button at the top of the panel, or {#action git::StageAll}.
Entries are grouped into sections (**Tracked** and **Untracked** by default), and each section can be staged on its own without touching the others. Click the checkbox on a section header, or use {#action git::StageSection} / {#action git::UnstageSection}, which act on the section containing the selected entry. These are also available by right-clicking an entry. Neither is bound by default; bind them in your keymap under the `GitPanel` context if you use them often.
To open a changed file in the editor without a diff view, right-click on the file in the Git Panel and select **View File**. Use **Open Diff** ({#kb menu::Confirm}) or **Open Diff (File)** to review changes in a diff view instead.
<!-- Add media -->
@@ -255,9 +257,13 @@ Git stash allows you to temporarily save your uncommitted changes and revert you
### Creating Stashes
To stash all your current changes, use the {#action git::StashAll} action. This will save both staged and unstaged changes to a new stash entry and clean your working directory.
To stash all your current changes, use the {#action git::StashAll} action. This will save both staged and unstaged changes, including untracked files, to a new stash entry and clean your working directory.
The action first prompts for an optional stash name. Confirming with an empty prompt lets git generate its usual `WIP on ...` description.
To stash only tracked changes and leave untracked files in place, use the {#action git::StashTracked} action. To stash only staged changes and leave unstaged changes in place, use the {#action git::StashStaged} action; this requires git 2.35 or newer.
The Git Panel's overflow menu offers whichever of these matches the current **Group By** mode: **Stash Tracked** when grouping by Tracked & Untracked, and **Stash Staged** when grouping by Staged & Unstaged. **Stash All** is always available.
Each of these actions first prompts for an optional stash name. Confirming with an empty prompt lets git generate its usual `WIP on ...` description.
### Managing Stashes
@@ -385,6 +391,8 @@ When viewing files with changes, Zed displays diff hunks that can be expanded or
| {#action git::Add} | {#kb git::Add} |
| {#action git::StageAll} | {#kb git::StageAll} |
| {#action git::UnstageAll} | {#kb git::UnstageAll} |
| {#action git::StageSection} | {#kb git::StageSection} |
| {#action git::UnstageSection} | {#kb git::UnstageSection} |
| {#action git::ToggleStaged} | {#kb git::ToggleStaged} |
| {#action git::StageAndNext} | {#kb git::StageAndNext} |
| {#action git::UnstageAndNext} | {#kb git::UnstageAndNext} |
@@ -404,6 +412,8 @@ When viewing files with changes, Zed displays diff hunks that can be expanded or
| {#action git::Worktree} | {#kb git::Worktree} |
| {#action git::Blame} | {#kb git::Blame} |
| {#action git::StashAll} | {#kb git::StashAll} |
| {#action git::StashTracked} | {#kb git::StashTracked} |
| {#action git::StashStaged} | {#kb git::StashStaged} |
| {#action git::StashPop} | {#kb git::StashPop} |
| {#action git::StashApply} | {#kb git::StashApply} |
| {#action git::ViewStash} | {#kb git::ViewStash} |