git: Enable git stash in git panel (#32821)

Related discussion #31484

Release Notes:

- Added a menu entry on the git panel to git stash and git pop stash. 

Preview: 


![Screenshot-2025-06-17_08:26:36](https://github.com/user-attachments/assets/d3699ba4-511f-4c7b-a7cc-00a295d01f64)

---------

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Alvaro Parker 2025-07-25 19:15:54 -04:00 committed by GitHub
parent 4d00d07df1
commit 07252c3309
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 290 additions and 2 deletions

View file

@ -27,7 +27,10 @@ use git::repository::{
};
use git::status::StageStatus;
use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus};
use git::{ExpandCommitEditor, RestoreTrackedFiles, StageAll, TrashUntrackedFiles, UnstageAll};
use git::{
ExpandCommitEditor, RestoreTrackedFiles, StageAll, StashAll, StashPop, TrashUntrackedFiles,
UnstageAll,
};
use gpui::{
Action, Animation, AnimationExt as _, AsyncApp, AsyncWindowContext, Axis, ClickEvent, Corner,
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, KeyContext,
@ -140,6 +143,13 @@ fn git_panel_context_menu(
UnstageAll.boxed_clone(),
)
.separator()
.action_disabled_when(
!(state.has_new_changes || state.has_tracked_changes),
"Stash All",
StashAll.boxed_clone(),
)
.action("Stash Pop", StashPop.boxed_clone())
.separator()
.action("Open Diff", project_diff::Diff.boxed_clone())
.separator()
.action_disabled_when(
@ -1415,6 +1425,52 @@ impl GitPanel {
self.tracked_staged_count + self.new_staged_count + self.conflicted_staged_count
}
pub fn stash_pop(&mut self, _: &StashPop, _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_pop(cx))?
.await;
this.update(cx, |this, cx| {
stash_task
.map_err(|e| {
this.show_error_toast("stash pop", 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;
};
cx.spawn({
async move |this, cx| {
let stash_task = active_repository
.update(cx, |repo, cx| repo.stash_all(cx))?
.await;
this.update(cx, |this, cx| {
stash_task
.map_err(|e| {
this.show_error_toast("stash", e, cx);
})
.ok();
cx.notify();
})
}
})
.detach();
}
pub fn commit_message_buffer(&self, cx: &App) -> Entity<Buffer> {
self.commit_editor
.read(cx)
@ -4365,6 +4421,8 @@ impl Render for GitPanel {
.on_action(cx.listener(Self::revert_selected))
.on_action(cx.listener(Self::clean_all))
.on_action(cx.listener(Self::generate_commit_message_action))
.on_action(cx.listener(Self::stash_all))
.on_action(cx.listener(Self::stash_pop))
})
.on_action(cx.listener(Self::select_first))
.on_action(cx.listener(Self::select_next))

View file

@ -114,6 +114,22 @@ pub fn init(cx: &mut App) {
});
});
}
workspace.register_action(|workspace, action: &git::StashAll, window, cx| {
let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else {
return;
};
panel.update(cx, |panel, cx| {
panel.stash_all(action, window, cx);
});
});
workspace.register_action(|workspace, action: &git::StashPop, window, cx| {
let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else {
return;
};
panel.update(cx, |panel, cx| {
panel.stash_pop(action, window, cx);
});
});
workspace.register_action(|workspace, action: &git::StageAll, window, cx| {
let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else {
return;