diff --git a/Cargo.lock b/Cargo.lock
index d1e3de16896d8f0283a7c4fca15003154e97ed2f..8637865620d3173c6a81007776197d5f6f18b96d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5353,6 +5353,7 @@ dependencies = [
"serde_json",
"smol",
"sum_tree",
+ "tempfile",
"text",
"time",
"unindent",
@@ -5408,7 +5409,9 @@ dependencies = [
"gpui",
"itertools 0.14.0",
"language",
+ "linkify",
"linkme",
+ "log",
"menu",
"multi_buffer",
"panel",
diff --git a/assets/icons/cloud.svg b/assets/icons/cloud.svg
new file mode 100644
index 0000000000000000000000000000000000000000..73a9618067bf933814e2dc2e9c53f56432e07839
--- /dev/null
+++ b/assets/icons/cloud.svg
@@ -0,0 +1 @@
+
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index c4cf22570916f09451467ddc705bab4a81f02a23..7f538bae11ab46528dba4e4a6361aebf40cb31a3 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -32,12 +32,12 @@
"ctrl-enter": "menu::SecondaryConfirm",
"cmd-enter": "menu::SecondaryConfirm",
"ctrl-escape": "menu::Cancel",
- "cmd-escape": "menu::Cancel",
"ctrl-c": "menu::Cancel",
"escape": "menu::Cancel",
"alt-shift-enter": "menu::Restart",
"cmd-shift-w": "workspace::CloseWindow",
"shift-escape": "workspace::ToggleZoom",
+ "cmd-escape": "menu::Cancel",
"cmd-o": "workspace::Open",
"cmd-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
"cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
diff --git a/crates/auto_update_ui/src/auto_update_ui.rs b/crates/auto_update_ui/src/auto_update_ui.rs
index 79ebdf9ce511aca0b0113042d74e52eefcf9f10e..adf55f17731db091c3bf792ef75cd6de2a6569b5 100644
--- a/crates/auto_update_ui/src/auto_update_ui.rs
+++ b/crates/auto_update_ui/src/auto_update_ui.rs
@@ -141,19 +141,20 @@ pub fn notify_if_app_was_updated(cx: &mut App) {
cx,
move |cx| {
let workspace_handle = cx.entity().downgrade();
- cx.new(|_cx| {
- MessageNotification::new(format!("Updated to {app_name} {}", version))
- .primary_message("View Release Notes")
- .primary_on_click(move |window, cx| {
- if let Some(workspace) = workspace_handle.upgrade() {
- workspace.update(cx, |workspace, cx| {
- crate::view_release_notes_locally(
- workspace, window, cx,
- );
- })
- }
- cx.emit(DismissEvent);
- })
+ cx.new(|cx| {
+ MessageNotification::new(
+ format!("Updated to {app_name} {}", version),
+ cx,
+ )
+ .primary_message("View Release Notes")
+ .primary_on_click(move |window, cx| {
+ if let Some(workspace) = workspace_handle.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ crate::view_release_notes_locally(workspace, window, cx);
+ })
+ }
+ cx.emit(DismissEvent);
+ })
})
},
);
diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs
index 24203ec98b1bf9f622d1ebeb14255d11ff61e360..53c8ce73173a6976452cffa4b21de813ea4bfbdf 100644
--- a/crates/breadcrumbs/src/breadcrumbs.rs
+++ b/crates/breadcrumbs/src/breadcrumbs.rs
@@ -82,7 +82,7 @@ impl Render for Breadcrumbs {
text_style.color = Color::Muted.color(cx);
StyledText::new(segment.text.replace('\n', "⏎"))
- .with_highlights(&text_style, segment.highlights.unwrap_or_default())
+ .with_default_highlights(&text_style, segment.highlights.unwrap_or_default())
.into_any()
});
let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs
index 50845af5ba63bd0b44d4166caefea179b04e886f..2abded65e81195d473c7e5a84f68d8d153bc2c2e 100644
--- a/crates/collab_ui/src/notification_panel.rs
+++ b/crates/collab_ui/src/notification_panel.rs
@@ -22,7 +22,7 @@ use ui::{
h_flex, prelude::*, v_flex, Avatar, Button, Icon, IconButton, IconName, Label, Tab, Tooltip,
};
use util::{ResultExt, TryFutureExt};
-use workspace::notifications::NotificationId;
+use workspace::notifications::{Notification as WorkspaceNotification, NotificationId};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
Workspace,
@@ -570,11 +570,12 @@ impl NotificationPanel {
workspace.dismiss_notification(&id, cx);
workspace.show_notification(id, cx, |cx| {
let workspace = cx.entity().downgrade();
- cx.new(|_| NotificationToast {
+ cx.new(|cx| NotificationToast {
notification_id,
actor,
text,
workspace,
+ focus_handle: cx.focus_handle(),
})
})
})
@@ -771,8 +772,17 @@ pub struct NotificationToast {
actor: Option>,
text: String,
workspace: WeakEntity,
+ focus_handle: FocusHandle,
+}
+
+impl Focusable for NotificationToast {
+ fn focus_handle(&self, _cx: &App) -> FocusHandle {
+ self.focus_handle.clone()
+ }
}
+impl WorkspaceNotification for NotificationToast {}
+
impl NotificationToast {
fn focus_notification_panel(&self, window: &mut Window, cx: &mut Context) {
let workspace = self.workspace.clone();
diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs
index 42b1800fe7e2f5a18355c02c8cb1ca7baf511b78..defe2950acae97274da50bb4c1f390927576e528 100644
--- a/crates/diagnostics/src/diagnostics.rs
+++ b/crates/diagnostics/src/diagnostics.rs
@@ -995,7 +995,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
h_flex()
.gap_1()
.child(
- StyledText::new(message.clone()).with_highlights(
+ StyledText::new(message.clone()).with_default_highlights(
&cx.window.text_style(),
code_ranges
.iter()
diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs
index bfb59699452d67dce82214fdad4ce950d69f4160..3155e6f3463b52a21e02cfaa2be83b50a37cec80 100644
--- a/crates/editor/src/code_context_menus.rs
+++ b/crates/editor/src/code_context_menus.rs
@@ -514,7 +514,7 @@ impl CompletionsMenu {
);
let completion_label = StyledText::new(completion.label.text.clone())
- .with_highlights(&style.text, highlights);
+ .with_default_highlights(&style.text, highlights);
let documentation_label = if let Some(
CompletionDocumentation::SingleLine(text),
) = documentation
diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
index fcc06861fc60022dde6b3168e511f4fb8aa7c632..a92265010e47e1b1e6c86303283c338ca5e16521 100644
--- a/crates/editor/src/editor.rs
+++ b/crates/editor/src/editor.rs
@@ -6781,7 +6781,7 @@ impl Editor {
.first_line_preview();
let styled_text = gpui::StyledText::new(highlighted_edits.text)
- .with_highlights(&style.text, highlighted_edits.highlights);
+ .with_default_highlights(&style.text, highlighted_edits.highlights);
let preview = h_flex()
.gap_1()
@@ -18007,7 +18007,7 @@ pub fn diagnostic_block_renderer(
)
.child(buttons(&diagnostic))
.child(div().flex().flex_shrink_0().child(
- StyledText::new(text_without_backticks.clone()).with_highlights(
+ StyledText::new(text_without_backticks.clone()).with_default_highlights(
&text_style,
code_ranges.iter().map(|range| {
(
diff --git a/crates/editor/src/signature_help.rs b/crates/editor/src/signature_help.rs
index dbad782766caf073b73c74254d2b517c7881c716..1e1dd15aa92bfce935a08b5ea8670afc10f43571 100644
--- a/crates/editor/src/signature_help.rs
+++ b/crates/editor/src/signature_help.rs
@@ -310,7 +310,7 @@ impl SignatureHelpPopover {
.child(
div().px_4().pb_1().child(
StyledText::new(self.label.clone())
- .with_highlights(&self.style, self.highlights.iter().cloned()),
+ .with_default_highlights(&self.style, self.highlights.iter().cloned()),
),
)
.into_any_element()
diff --git a/crates/extensions_ui/src/extension_suggest.rs b/crates/extensions_ui/src/extension_suggest.rs
index 4844dce7558837de1dd98776467d299e0af44b1e..32e61de6eff396f7c1dc63a3823ebc87c7e7b455 100644
--- a/crates/extensions_ui/src/extension_suggest.rs
+++ b/crates/extensions_ui/src/extension_suggest.rs
@@ -168,11 +168,14 @@ pub(crate) fn suggest(buffer: Entity, window: &mut Window, cx: &mut Cont
);
workspace.show_notification(notification_id, cx, |cx| {
- cx.new(move |_cx| {
- MessageNotification::new(format!(
- "Do you want to install the recommended '{}' extension for '{}' files?",
- extension_id, file_name_or_extension
- ))
+ cx.new(move |cx| {
+ MessageNotification::new(
+ format!(
+ "Do you want to install the recommended '{}' extension for '{}' files?",
+ extension_id, file_name_or_extension
+ ),
+ cx,
+ )
.primary_message("Yes, install extension")
.primary_icon(IconName::Check)
.primary_icon_color(Color::Success)
diff --git a/crates/file_finder/src/new_path_prompt.rs b/crates/file_finder/src/new_path_prompt.rs
index 420238493d3d33f9abb40de39f5ced7e28cbbcf6..05b336fc45909e4348aa50f6a86c6af1a3946b06 100644
--- a/crates/file_finder/src/new_path_prompt.rs
+++ b/crates/file_finder/src/new_path_prompt.rs
@@ -192,7 +192,7 @@ impl Match {
}
}
- StyledText::new(text).with_highlights(&window.text_style().clone(), highlights)
+ StyledText::new(text).with_default_highlights(&window.text_style().clone(), highlights)
}
}
diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml
index 0473b1dd57d26907465dce7e82477ea22858c47d..f32d704b3e043d1e98f11fbaf6a454d01a679f21 100644
--- a/crates/git/Cargo.toml
+++ b/crates/git/Cargo.toml
@@ -34,6 +34,7 @@ text.workspace = true
time.workspace = true
url.workspace = true
util.workspace = true
+tempfile.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true
diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs
index b690887ca1fa392de24dc851f027c6e254ed313f..b7ef907b575aed890d85278455799213711a130c 100644
--- a/crates/git/src/repository.rs
+++ b/crates/git/src/repository.rs
@@ -11,6 +11,8 @@ use schemars::JsonSchema;
use serde::Deserialize;
use std::borrow::Borrow;
use std::io::Write as _;
+#[cfg(not(windows))]
+use std::os::unix::fs::PermissionsExt;
use std::process::Stdio;
use std::sync::LazyLock;
use std::{
@@ -61,6 +63,12 @@ pub enum UpstreamTracking {
Tracked(UpstreamTrackingStatus),
}
+impl From for UpstreamTracking {
+ fn from(status: UpstreamTrackingStatus) -> Self {
+ UpstreamTracking::Tracked(status)
+ }
+}
+
impl UpstreamTracking {
pub fn is_gone(&self) -> bool {
matches!(self, UpstreamTracking::Gone)
@@ -74,9 +82,15 @@ impl UpstreamTracking {
}
}
-impl From for UpstreamTracking {
- fn from(status: UpstreamTrackingStatus) -> Self {
- UpstreamTracking::Tracked(status)
+#[derive(Debug)]
+pub struct RemoteCommandOutput {
+ pub stdout: String,
+ pub stderr: String,
+}
+
+impl RemoteCommandOutput {
+ pub fn is_empty(&self) -> bool {
+ self.stdout.is_empty() && self.stderr.is_empty()
}
}
@@ -185,10 +199,10 @@ pub trait GitRepository: Send + Sync {
branch_name: &str,
upstream_name: &str,
options: Option,
- ) -> Result<()>;
- fn pull(&self, branch_name: &str, upstream_name: &str) -> Result<()>;
+ ) -> Result;
+ fn pull(&self, branch_name: &str, upstream_name: &str) -> Result;
fn get_remotes(&self, branch_name: Option<&str>) -> Result>;
- fn fetch(&self) -> Result<()>;
+ fn fetch(&self) -> Result;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)]
@@ -611,19 +625,30 @@ impl GitRepository for RealGitRepository {
branch_name: &str,
remote_name: &str,
options: Option,
- ) -> Result<()> {
+ ) -> Result {
let working_directory = self.working_directory()?;
- let output = new_std_command("git")
+ // We do this on every operation to ensure that the askpass script exists and is executable.
+ #[cfg(not(windows))]
+ let (askpass_script_path, _temp_dir) = setup_askpass()?;
+
+ let mut command = new_std_command("git");
+ command
.current_dir(&working_directory)
- .args(["push", "--quiet"])
+ .args(["push"])
.args(options.map(|option| match option {
PushOptions::SetUpstream => "--set-upstream",
PushOptions::Force => "--force-with-lease",
}))
.arg(remote_name)
- .arg(format!("{}:{}", branch_name, branch_name))
- .output()?;
+ .arg(format!("{}:{}", branch_name, branch_name));
+
+ #[cfg(not(windows))]
+ {
+ command.env("GIT_ASKPASS", askpass_script_path);
+ }
+
+ let output = command.output()?;
if !output.status.success() {
return Err(anyhow!(
@@ -631,19 +656,33 @@ impl GitRepository for RealGitRepository {
String::from_utf8_lossy(&output.stderr)
));
} else {
- Ok(())
+ return Ok(RemoteCommandOutput {
+ stdout: String::from_utf8_lossy(&output.stdout).to_string(),
+ stderr: String::from_utf8_lossy(&output.stderr).to_string(),
+ });
}
}
- fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> {
+ fn pull(&self, branch_name: &str, remote_name: &str) -> Result {
let working_directory = self.working_directory()?;
- let output = new_std_command("git")
+ // We do this on every operation to ensure that the askpass script exists and is executable.
+ #[cfg(not(windows))]
+ let (askpass_script_path, _temp_dir) = setup_askpass()?;
+
+ let mut command = new_std_command("git");
+ command
.current_dir(&working_directory)
- .args(["pull", "--quiet"])
+ .args(["pull"])
.arg(remote_name)
- .arg(branch_name)
- .output()?;
+ .arg(branch_name);
+
+ #[cfg(not(windows))]
+ {
+ command.env("GIT_ASKPASS", askpass_script_path);
+ }
+
+ let output = command.output()?;
if !output.status.success() {
return Err(anyhow!(
@@ -651,17 +690,31 @@ impl GitRepository for RealGitRepository {
String::from_utf8_lossy(&output.stderr)
));
} else {
- return Ok(());
+ return Ok(RemoteCommandOutput {
+ stdout: String::from_utf8_lossy(&output.stdout).to_string(),
+ stderr: String::from_utf8_lossy(&output.stderr).to_string(),
+ });
}
}
- fn fetch(&self) -> Result<()> {
+ fn fetch(&self) -> Result {
let working_directory = self.working_directory()?;
- let output = new_std_command("git")
+ // We do this on every operation to ensure that the askpass script exists and is executable.
+ #[cfg(not(windows))]
+ let (askpass_script_path, _temp_dir) = setup_askpass()?;
+
+ let mut command = new_std_command("git");
+ command
.current_dir(&working_directory)
- .args(["fetch", "--quiet", "--all"])
- .output()?;
+ .args(["fetch", "--all"]);
+
+ #[cfg(not(windows))]
+ {
+ command.env("GIT_ASKPASS", askpass_script_path);
+ }
+
+ let output = command.output()?;
if !output.status.success() {
return Err(anyhow!(
@@ -669,7 +722,10 @@ impl GitRepository for RealGitRepository {
String::from_utf8_lossy(&output.stderr)
));
} else {
- return Ok(());
+ return Ok(RemoteCommandOutput {
+ stdout: String::from_utf8_lossy(&output.stdout).to_string(),
+ stderr: String::from_utf8_lossy(&output.stderr).to_string(),
+ });
}
}
@@ -716,6 +772,18 @@ impl GitRepository for RealGitRepository {
}
}
+#[cfg(not(windows))]
+fn setup_askpass() -> Result<(PathBuf, tempfile::TempDir), anyhow::Error> {
+ let temp_dir = tempfile::Builder::new()
+ .prefix("zed-git-askpass")
+ .tempdir()?;
+ let askpass_script = "#!/bin/sh\necho ''";
+ let askpass_script_path = temp_dir.path().join("git-askpass.sh");
+ std::fs::write(&askpass_script_path, askpass_script)?;
+ std::fs::set_permissions(&askpass_script_path, std::fs::Permissions::from_mode(0o755))?;
+ Ok((askpass_script_path, temp_dir))
+}
+
#[derive(Debug, Clone)]
pub struct FakeGitRepository {
state: Arc>,
@@ -899,15 +967,20 @@ impl GitRepository for FakeGitRepository {
unimplemented!()
}
- fn push(&self, _branch: &str, _remote: &str, _options: Option) -> Result<()> {
+ fn push(
+ &self,
+ _branch: &str,
+ _remote: &str,
+ _options: Option,
+ ) -> Result {
unimplemented!()
}
- fn pull(&self, _branch: &str, _remote: &str) -> Result<()> {
+ fn pull(&self, _branch: &str, _remote: &str) -> Result {
unimplemented!()
}
- fn fetch(&self) -> Result<()> {
+ fn fetch(&self) -> Result {
unimplemented!()
}
diff --git a/crates/git_ui/Cargo.toml b/crates/git_ui/Cargo.toml
index 66845c939f8df2cd0c8768bf775e0a5355684866..f5b86abd15d6aff6dde6bdcd014f1e8429d177b8 100644
--- a/crates/git_ui/Cargo.toml
+++ b/crates/git_ui/Cargo.toml
@@ -30,7 +30,9 @@ git.workspace = true
gpui.workspace = true
itertools.workspace = true
language.workspace = true
+linkify.workspace = true
linkme.workspace = true
+log.workspace = true
menu.workspace = true
multi_buffer.workspace = true
panel.workspace = true
diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs
index dd2082fcc83f78236eb8613be1e79eeea3992b85..6fe12b3c382b63334ff03e83fc8a8910e0cd1c96 100644
--- a/crates/git_ui/src/git_panel.rs
+++ b/crates/git_ui/src/git_panel.rs
@@ -1,5 +1,6 @@
use crate::branch_picker::{self, BranchList};
use crate::git_panel_settings::StatusStyle;
+use crate::remote_output_toast::{RemoteAction, RemoteOutputToast};
use crate::repository_selector::RepositorySelectorPopoverMenu;
use crate::{
git_panel_settings::GitPanelSettings, git_status_icon, repository_selector::RepositorySelector,
@@ -12,8 +13,8 @@ use editor::{
ShowScrollbar,
};
use git::repository::{
- Branch, CommitDetails, CommitSummary, PushOptions, Remote, ResetMode, Upstream,
- UpstreamTracking, UpstreamTrackingStatus,
+ Branch, CommitDetails, CommitSummary, PushOptions, Remote, RemoteCommandOutput, ResetMode,
+ Upstream, UpstreamTracking, UpstreamTrackingStatus,
};
use git::{repository::RepoPath, status::FileStatus, Commit, ToggleStaged};
use git::{RestoreTrackedFiles, StageAll, TrashUntrackedFiles, UnstageAll};
@@ -43,6 +44,7 @@ use ui::{
PopoverButton, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
};
use util::{maybe, post_inc, ResultExt, TryFutureExt};
+
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
notifications::{DetachAndPromptErr, NotificationId},
@@ -283,6 +285,7 @@ impl GitPanel {
let commit_editor = cx.new(|cx| {
commit_message_editor(temporary_buffer, None, project.clone(), true, window, cx)
});
+
commit_editor.update(cx, |editor, cx| {
editor.clear(window, cx);
});
@@ -1330,62 +1333,114 @@ impl GitPanel {
};
let guard = self.start_remote_operation();
let fetch = repo.read(cx).fetch();
- cx.spawn(|_, _| async move {
- fetch.await??;
+ cx.spawn(|this, mut cx| async move {
+ let remote_message = fetch.await?;
drop(guard);
+ this.update(&mut cx, |this, cx| {
+ match remote_message {
+ Ok(remote_message) => {
+ this.show_remote_output(RemoteAction::Fetch, remote_message, cx);
+ }
+ Err(e) => {
+ this.show_err_toast(e, cx);
+ }
+ }
+
+ anyhow::Ok(())
+ })
+ .ok();
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
fn pull(&mut self, _: &git::Pull, window: &mut Window, cx: &mut Context) {
+ let Some(repo) = self.active_repository.clone() else {
+ return;
+ };
+ let Some(branch) = repo.read(cx).current_branch() else {
+ return;
+ };
+ let branch = branch.clone();
let guard = self.start_remote_operation();
let remote = self.get_current_remote(window, cx);
cx.spawn(move |this, mut cx| async move {
- let remote = remote.await?;
+ let remote = match remote.await {
+ Ok(Some(remote)) => remote,
+ Ok(None) => {
+ return Ok(());
+ }
+ Err(e) => {
+ log::error!("Failed to get current remote: {}", e);
+ this.update(&mut cx, |this, cx| this.show_err_toast(e, cx))
+ .ok();
+ return Ok(());
+ }
+ };
- this.update(&mut cx, |this, cx| {
- let Some(repo) = this.active_repository.clone() else {
- return Err(anyhow::anyhow!("No active repository"));
- };
+ let pull = repo.update(&mut cx, |repo, _cx| {
+ repo.pull(branch.name.clone(), remote.name.clone())
+ })?;
- let Some(branch) = repo.read(cx).current_branch() else {
- return Err(anyhow::anyhow!("No active branch"));
- };
+ let remote_message = pull.await?;
+ drop(guard);
- Ok(repo.read(cx).pull(branch.name.clone(), remote.name))
- })??
- .await??;
+ this.update(&mut cx, |this, cx| match remote_message {
+ Ok(remote_message) => {
+ this.show_remote_output(RemoteAction::Pull, remote_message, cx)
+ }
+ Err(err) => this.show_err_toast(err, cx),
+ })
+ .ok();
- drop(guard);
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
fn push(&mut self, action: &git::Push, window: &mut Window, cx: &mut Context) {
+ let Some(repo) = self.active_repository.clone() else {
+ return;
+ };
+ let Some(branch) = repo.read(cx).current_branch() else {
+ return;
+ };
+ let branch = branch.clone();
let guard = self.start_remote_operation();
let options = action.options;
let remote = self.get_current_remote(window, cx);
- cx.spawn(move |this, mut cx| async move {
- let remote = remote.await?;
- this.update(&mut cx, |this, cx| {
- let Some(repo) = this.active_repository.clone() else {
- return Err(anyhow::anyhow!("No active repository"));
- };
+ cx.spawn(move |this, mut cx| async move {
+ let remote = match remote.await {
+ Ok(Some(remote)) => remote,
+ Ok(None) => {
+ return Ok(());
+ }
+ Err(e) => {
+ log::error!("Failed to get current remote: {}", e);
+ this.update(&mut cx, |this, cx| this.show_err_toast(e, cx))
+ .ok();
+ return Ok(());
+ }
+ };
- let Some(branch) = repo.read(cx).current_branch() else {
- return Err(anyhow::anyhow!("No active branch"));
- };
+ let push = repo.update(&mut cx, |repo, _cx| {
+ repo.push(branch.name.clone(), remote.name.clone(), options)
+ })?;
- Ok(repo
- .read(cx)
- .push(branch.name.clone(), remote.name, options))
- })??
- .await??;
+ let remote_output = push.await?;
drop(guard);
+
+ this.update(&mut cx, |this, cx| match remote_output {
+ Ok(remote_message) => {
+ this.show_remote_output(RemoteAction::Push(remote), remote_message, cx);
+ }
+ Err(e) => {
+ this.show_err_toast(e, cx);
+ }
+ })?;
+
anyhow::Ok(())
})
.detach_and_log_err(cx);
@@ -1395,7 +1450,7 @@ impl GitPanel {
&mut self,
window: &mut Window,
cx: &mut Context,
- ) -> impl Future