git_ui: Add button for the `branch diff` action (#49298)

Danilo Leal created

This PR adds a button in the Git Panel to open the `git: branch diff`
view. The button only shows if the staging area is clean/no changes and
if the branch is not main/master. I've been using this action so much
these days and we should expose it on the UI so it's more discoverable.

<img width="500" height="1736" alt="Screenshot 2026-02-16 at 6  30@2x"
src="https://github.com/user-attachments/assets/0b037811-2167-419f-b63f-9bdb12e4352e"
/>

Release Notes:

- N/A

Change summary

crates/git_ui/src/git_panel.rs | 99 ++++++++++++++++++++++++------------
1 file changed, 66 insertions(+), 33 deletions(-)

Detailed changes

crates/git_ui/src/git_panel.rs 🔗

@@ -2,7 +2,7 @@ use crate::askpass_modal::AskPassModal;
 use crate::commit_modal::CommitModal;
 use crate::commit_tooltip::CommitTooltip;
 use crate::commit_view::CommitView;
-use crate::project_diff::{self, Diff, ProjectDiff};
+use crate::project_diff::{self, BranchDiff, Diff, ProjectDiff};
 use crate::remote_output::{self, RemoteAction, SuccessMessage};
 use crate::{branch_picker, picker_prompt, render_remote_button};
 use crate::{
@@ -4566,38 +4566,71 @@ impl GitPanel {
     }
 
     fn render_empty_state(&self, cx: &mut Context<Self>) -> impl IntoElement {
-        h_flex().h_full().flex_grow().justify_center().child(
-            v_flex()
-                .gap_2()
-                .child(h_flex().w_full().justify_around().child(
-                    if self.active_repository.is_some() {
-                        "No changes to commit"
-                    } else {
-                        "No Git repositories"
-                    },
-                ))
-                .children({
-                    let worktree_count = self.project.read(cx).visible_worktrees(cx).count();
-                    (worktree_count > 0 && self.active_repository.is_none()).then(|| {
-                        h_flex().w_full().justify_around().child(
-                            panel_filled_button("Initialize Repository")
-                                .tooltip(Tooltip::for_action_title_in(
-                                    "git init",
-                                    &git::Init,
-                                    &self.focus_handle,
-                                ))
-                                .on_click(move |_, _, cx| {
-                                    cx.defer(move |cx| {
-                                        cx.dispatch_action(&git::Init);
-                                    })
-                                }),
-                        )
-                    })
-                })
-                .text_ui_sm(cx)
-                .mx_auto()
-                .text_color(Color::Placeholder.color(cx)),
-        )
+        let has_repo = self.active_repository.is_some();
+        let has_no_repo = self.active_repository.is_none();
+        let worktree_count = self.project.read(cx).visible_worktrees(cx).count();
+
+        let should_show_branch_diff =
+            has_repo && self.changes_count == 0 && !self.is_on_main_branch(cx);
+
+        let label = if has_repo {
+            "No changes to commit"
+        } else {
+            "No Git repositories"
+        };
+
+        v_flex()
+            .gap_1p5()
+            .flex_1()
+            .items_center()
+            .justify_center()
+            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted))
+            .when(has_no_repo && worktree_count > 0, |this| {
+                this.child(
+                    panel_filled_button("Initialize Repository")
+                        .tooltip(Tooltip::for_action_title_in(
+                            "git init",
+                            &git::Init,
+                            &self.focus_handle,
+                        ))
+                        .on_click(move |_, _, cx| {
+                            cx.defer(move |cx| {
+                                cx.dispatch_action(&git::Init);
+                            })
+                        }),
+                )
+            })
+            .when(should_show_branch_diff, |this| {
+                this.child(
+                    panel_filled_button("View Branch Diff")
+                        .tooltip(move |_, cx| {
+                            Tooltip::with_meta(
+                                "Branch Diff",
+                                Some(&BranchDiff),
+                                "Show diff between working directory and default branch",
+                                cx,
+                            )
+                        })
+                        .on_click(move |_, _, cx| {
+                            cx.defer(move |cx| {
+                                cx.dispatch_action(&BranchDiff);
+                            })
+                        }),
+                )
+            })
+    }
+
+    fn is_on_main_branch(&self, cx: &Context<Self>) -> bool {
+        let Some(repo) = self.active_repository.as_ref() else {
+            return false;
+        };
+
+        let Some(branch) = repo.read(cx).branch.as_ref() else {
+            return false;
+        };
+
+        let branch_name = branch.name();
+        matches!(branch_name, "main" | "master")
     }
 
     fn render_buffer_header_controls(