git: Add git stash picker (#35927)
Closes #ISSUE This PR continues work from #32821 by adding a stash entry picker for pop/drop operations. Additionally, the stash pop action in the git panel is now disabled when no stash entries exist, preventing error logs from attempted pops on empty stashes. Preview: <img width="1920" height="1256" alt="Screenshot From 2025-09-11 14-08-31" src="https://github.com/user-attachments/assets/b2f32974-8c69-4e50-8951-24ab2cf93c12" /> <img width="1920" height="1256" alt="Screenshot From 2025-09-11 14-08-12" src="https://github.com/user-attachments/assets/992ce237-43c9-456e-979c-c2e2149d633e" /> Release Notes: - Added a stash picker to pop and drop a specific stash entry - Disabled the stash pop action on the git panel when no stash entries exist - Added git stash apply command - Added git stash drop command
This commit is contained in:
@@ -24,11 +24,12 @@ use git::repository::{
|
||||
PushOptions, Remote, RemoteCommandOutput, ResetMode, Upstream, UpstreamTracking,
|
||||
UpstreamTrackingStatus, get_git_committer,
|
||||
};
|
||||
use git::stash::GitStash;
|
||||
use git::status::StageStatus;
|
||||
use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus};
|
||||
use git::{
|
||||
ExpandCommitEditor, RestoreTrackedFiles, StageAll, StashAll, StashPop, TrashUntrackedFiles,
|
||||
UnstageAll,
|
||||
ExpandCommitEditor, RestoreTrackedFiles, StageAll, StashAll, StashApply, StashPop,
|
||||
TrashUntrackedFiles, UnstageAll,
|
||||
};
|
||||
use gpui::{
|
||||
Action, AsyncApp, AsyncWindowContext, Axis, ClickEvent, Corner, DismissEvent, Entity,
|
||||
@@ -121,6 +122,7 @@ struct GitMenuState {
|
||||
has_unstaged_changes: bool,
|
||||
has_new_changes: bool,
|
||||
sort_by_path: bool,
|
||||
has_stash_items: bool,
|
||||
}
|
||||
|
||||
fn git_panel_context_menu(
|
||||
@@ -148,7 +150,8 @@ fn git_panel_context_menu(
|
||||
"Stash All",
|
||||
StashAll.boxed_clone(),
|
||||
)
|
||||
.action("Stash Pop", StashPop.boxed_clone())
|
||||
.action_disabled_when(!state.has_stash_items, "Stash Pop", StashPop.boxed_clone())
|
||||
.action("View Stash", zed_actions::git::ViewStash.boxed_clone())
|
||||
.separator()
|
||||
.action("Open Diff", project_diff::Diff.boxed_clone())
|
||||
.separator()
|
||||
@@ -382,6 +385,7 @@ pub struct GitPanel {
|
||||
local_committer: Option<GitCommitter>,
|
||||
local_committer_task: Option<Task<()>>,
|
||||
bulk_staging: Option<BulkStaging>,
|
||||
stash_entries: GitStash,
|
||||
_settings_subscription: Subscription,
|
||||
}
|
||||
|
||||
@@ -569,6 +573,7 @@ impl GitPanel {
|
||||
horizontal_scrollbar,
|
||||
vertical_scrollbar,
|
||||
bulk_staging: None,
|
||||
stash_entries: Default::default(),
|
||||
_settings_subscription,
|
||||
};
|
||||
|
||||
@@ -1438,7 +1443,7 @@ impl GitPanel {
|
||||
cx.spawn({
|
||||
async move |this, cx| {
|
||||
let stash_task = active_repository
|
||||
.update(cx, |repo, cx| repo.stash_pop(cx))?
|
||||
.update(cx, |repo, cx| repo.stash_pop(None, cx))?
|
||||
.await;
|
||||
this.update(cx, |this, cx| {
|
||||
stash_task
|
||||
@@ -1453,6 +1458,29 @@ impl GitPanel {
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn stash_apply(&mut self, _: &StashApply, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(active_repository) = self.active_repository.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
cx.spawn({
|
||||
async move |this, cx| {
|
||||
let stash_task = active_repository
|
||||
.update(cx, |repo, cx| repo.stash_apply(None, cx))?
|
||||
.await;
|
||||
this.update(cx, |this, cx| {
|
||||
stash_task
|
||||
.map_err(|e| {
|
||||
this.show_error_toast("stash apply", e, cx);
|
||||
})
|
||||
.ok();
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn stash_all(&mut self, _: &StashAll, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(active_repository) = self.active_repository.clone() else {
|
||||
return;
|
||||
@@ -2734,6 +2762,8 @@ impl GitPanel {
|
||||
|
||||
let repo = repo.read(cx);
|
||||
|
||||
self.stash_entries = repo.cached_stash();
|
||||
|
||||
for entry in repo.cached_status() {
|
||||
let is_conflict = repo.had_conflict_on_last_merge_head_change(&entry.repo_path);
|
||||
let is_new = entry.status.is_created();
|
||||
@@ -3102,6 +3132,7 @@ impl GitPanel {
|
||||
let has_staged_changes = self.has_staged_changes();
|
||||
let has_unstaged_changes = self.has_unstaged_changes();
|
||||
let has_new_changes = self.new_count > 0;
|
||||
let has_stash_items = self.stash_entries.entries.len() > 0;
|
||||
|
||||
PopoverMenu::new(id.into())
|
||||
.trigger(
|
||||
@@ -3118,6 +3149,7 @@ impl GitPanel {
|
||||
has_unstaged_changes,
|
||||
has_new_changes,
|
||||
sort_by_path: GitPanelSettings::get_global(cx).sort_by_path,
|
||||
has_stash_items,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -4173,6 +4205,7 @@ impl GitPanel {
|
||||
has_unstaged_changes: self.has_unstaged_changes(),
|
||||
has_new_changes: self.new_count > 0,
|
||||
sort_by_path: GitPanelSettings::get_global(cx).sort_by_path,
|
||||
has_stash_items: self.stash_entries.entries.len() > 0,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
|
||||
Reference in New Issue
Block a user