From 70a742eadcf8968f80378ec4e424d7cc45860cef Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Mon, 16 Mar 2026 12:30:32 -0300 Subject: [PATCH] git_ui: Don't display the merge conflict notification if an agent is running (#51498) This PR is motivated by internal feedback in which the notification that we show inviting to resolve merging conflicts with an agent also pops up if the agent itself ran `git merge`. In this case, the notification is unnecessary noise. So, what I'm doing here is simply _not_ showing it if there's a running agent. I want to note that this change is accepting a trade-off here, in which there could be cases that even if an agent is running, the notification can still be useful. There could be other ways to identify whether the agent is running `git merge`, but they all felt a bit too complex for the moment. And given this is reasonably an edge case, I'm favoring a simple approach for now. Release Notes: - N/A --------- Co-authored-by: Lukas Wirth --- .../src/connection_view/thread_view.rs | 26 +++++++++++++++++-- crates/git_ui/src/conflict_view.rs | 17 +++++------- crates/workspace/src/notifications.rs | 8 ++++++ crates/workspace/src/workspace.rs | 6 +++++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/connection_view/thread_view.rs index c5e44a582ce..29ba06f470d 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/crates/agent_ui/src/connection_view/thread_view.rs @@ -739,10 +739,13 @@ impl ThreadView { } } })); + if self.parent_id.is_none() { + self.suppress_merge_conflict_notification(cx); + } generation } - pub fn stop_turn(&mut self, generation: usize) { + pub fn stop_turn(&mut self, generation: usize, cx: &mut Context) { if self.turn_fields.turn_generation != generation { return; } @@ -753,6 +756,25 @@ impl ThreadView { .map(|started| started.elapsed()); self.turn_fields.last_turn_tokens = self.turn_fields.turn_tokens.take(); self.turn_fields._turn_timer_task = None; + if self.parent_id.is_none() { + self.unsuppress_merge_conflict_notification(cx); + } + } + + fn suppress_merge_conflict_notification(&self, cx: &mut Context) { + self.workspace + .update(cx, |workspace, cx| { + workspace.suppress_notification(&workspace::merge_conflict_notification_id(), cx); + }) + .ok(); + } + + fn unsuppress_merge_conflict_notification(&self, cx: &mut Context) { + self.workspace + .update(cx, |workspace, _cx| { + workspace.unsuppress(workspace::merge_conflict_notification_id()); + }) + .ok(); } pub fn update_turn_tokens(&mut self, cx: &App) { @@ -962,7 +984,7 @@ impl ThreadView { let mut cx = cx.clone(); move || { this.update(&mut cx, |this, cx| { - this.stop_turn(generation); + this.stop_turn(generation, cx); cx.notify(); }) .ok(); diff --git a/crates/git_ui/src/conflict_view.rs b/crates/git_ui/src/conflict_view.rs index d3bb5213a5c..96faa8879b3 100644 --- a/crates/git_ui/src/conflict_view.rs +++ b/crates/git_ui/src/conflict_view.rs @@ -18,10 +18,7 @@ use settings::Settings; use std::{cell::RefCell, ops::Range, rc::Rc, sync::Arc}; use ui::{ActiveTheme, Divider, Element as _, Styled, Window, prelude::*}; use util::{ResultExt as _, debug_panic, maybe}; -use workspace::{ - Workspace, - notifications::{NotificationId, simple_message_notification::MessageNotification}, -}; +use workspace::{Workspace, notifications::simple_message_notification::MessageNotification}; use zed_actions::agent::{ ConflictContent, ResolveConflictedFilesWithAgent, ResolveConflictsWithAgent, }; @@ -500,12 +497,6 @@ fn render_conflict_buttons( .into_any() } -struct MergeConflictNotification; - -fn merge_conflict_notification_id() -> NotificationId { - NotificationId::unique::() -} - fn collect_conflicted_file_paths(workspace: &Workspace, cx: &App) -> Vec { let project = workspace.project().read(cx); let git_store = project.git_store().read(cx); @@ -547,8 +538,12 @@ pub(crate) fn register_conflict_notification( return; } + if workspace.is_notification_suppressed(workspace::merge_conflict_notification_id()) { + return; + } + let paths = collect_conflicted_file_paths(workspace, cx); - let notification_id = merge_conflict_notification_id(); + let notification_id = workspace::merge_conflict_notification_id(); let current_paths_set: HashSet = paths.iter().cloned().collect(); if paths.is_empty() { diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 29bb9d7b063..85b1fe4e707 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -234,6 +234,14 @@ impl Workspace { self.suppressed_notifications.insert(id.clone()); } + pub fn is_notification_suppressed(&self, notification_id: NotificationId) -> bool { + self.suppressed_notifications.contains(¬ification_id) + } + + pub fn unsuppress(&mut self, notification_id: NotificationId) { + self.suppressed_notifications.remove(¬ification_id); + } + pub fn show_initial_notifications(&mut self, cx: &mut Context) { // Allow absence of the global so that tests don't need to initialize it. let app_notifications = GLOBAL_APP_NOTIFICATIONS diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7696af97996..dc3d076bd6a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -7268,6 +7268,12 @@ impl GlobalAnyActiveCall { cx.global() } } + +pub fn merge_conflict_notification_id() -> NotificationId { + struct MergeConflictNotification; + NotificationId::unique::() +} + /// Workspace-local view of a remote participant's location. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ParticipantLocation {