From 5f92b31a8a1ccb663c003ce61436e4517c58b9cb Mon Sep 17 00:00:00 2001
From: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
Date: Fri, 9 Jan 2026 10:36:16 -0500
Subject: [PATCH] git: Fix a bug where BranchDiff shows incorrect diffs
(#46433)
This happens when a user's default branch has different remote and local
versions on their system. Because we would get a diff using just a
branch name without including it's remote name as well
This is a better default because it's far less confusing from a user's
perspective to debug what's going on.
Release Notes:
- git: Fix BranchDiff showing incorrect diffs when default local branch
is out of sync with the remote default branch
---
crates/fs/src/fake_git_repo.rs | 14 ++++++++++++--
crates/git/src/repository.rs | 26 ++++++++++++++++++++++----
crates/git_ui/src/branch_picker.rs | 6 +++---
crates/git_ui/src/project_diff.rs | 2 +-
crates/git_ui/src/worktree_picker.rs | 6 +++---
crates/project/src/git_store.rs | 9 ++++++---
6 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/crates/fs/src/fake_git_repo.rs b/crates/fs/src/fake_git_repo.rs
index a83e8197d26d5ca41cc46b998c9e6f65ab8c701f..347419ef9a862cca6662716137bb86e9e3c11a92 100644
--- a/crates/fs/src/fake_git_repo.rs
+++ b/crates/fs/src/fake_git_repo.rs
@@ -720,8 +720,18 @@ impl GitRepository for FakeGitRepository {
unimplemented!()
}
- fn default_branch(&self) -> BoxFuture<'_, Result>> {
- async { Ok(Some("main".into())) }.boxed()
+ fn default_branch(
+ &self,
+ include_remote_name: bool,
+ ) -> BoxFuture<'_, Result >> {
+ async move {
+ Ok(Some(if include_remote_name {
+ "origin/main".into()
+ } else {
+ "main".into()
+ }))
+ }
+ .boxed()
}
fn create_remote(&self, name: String, url: String) -> BoxFuture<'_, Result<()>> {
diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs
index 6889acd8700ed96032f57f185c870fcf2139c313..04f345ab9e73192489f9c5d0e39b5c02b5642237 100644
--- a/crates/git/src/repository.rs
+++ b/crates/git/src/repository.rs
@@ -649,7 +649,10 @@ pub trait GitRepository: Send + Sync {
target_checkpoint: GitRepositoryCheckpoint,
) -> BoxFuture<'_, Result>;
- fn default_branch(&self) -> BoxFuture<'_, Result>>;
+ fn default_branch(
+ &self,
+ include_remote_name: bool,
+ ) -> BoxFuture<'_, Result >>;
}
pub enum DiffType {
@@ -2305,7 +2308,10 @@ impl GitRepository for RealGitRepository {
.boxed()
}
- fn default_branch(&self) -> BoxFuture<'_, Result >> {
+ fn default_branch(
+ &self,
+ include_remote_name: bool,
+ ) -> BoxFuture<'_, Result >> {
let working_directory = self.working_directory();
let git_binary_path = self.any_git_binary_path.clone();
@@ -2315,19 +2321,31 @@ impl GitRepository for RealGitRepository {
let working_directory = working_directory?;
let git = GitBinary::new(git_binary_path, working_directory, executor);
+ let strip_prefix = if include_remote_name {
+ "refs/remotes/"
+ } else {
+ "refs/remotes/upstream/"
+ };
+
if let Ok(output) = git
.run(&["symbolic-ref", "refs/remotes/upstream/HEAD"])
.await
{
let output = output
- .strip_prefix("refs/remotes/upstream/")
+ .strip_prefix(strip_prefix)
.map(|s| SharedString::from(s.to_owned()));
return Ok(output);
}
+ let strip_prefix = if include_remote_name {
+ "refs/remotes/"
+ } else {
+ "refs/remotes/origin/"
+ };
+
if let Ok(output) = git.run(&["symbolic-ref", "refs/remotes/origin/HEAD"]).await {
return Ok(output
- .strip_prefix("refs/remotes/origin/")
+ .strip_prefix(strip_prefix)
.map(|s| SharedString::from(s.to_owned())));
}
diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs
index 7d8e63081ba56c4b3814c4cdde9539491d5b6971..befcf83d1f93432ef8e05aa8d15bf77c6ea7d0e0 100644
--- a/crates/git_ui/src/branch_picker.rs
+++ b/crates/git_ui/src/branch_picker.rs
@@ -128,9 +128,9 @@ impl BranchList {
let all_branches_request = repository
.clone()
.map(|repository| repository.update(cx, |repository, _| repository.branches()));
- let default_branch_request = repository
- .clone()
- .map(|repository| repository.update(cx, |repository, _| repository.default_branch()));
+ let default_branch_request = repository.clone().map(|repository| {
+ repository.update(cx, |repository, _| repository.default_branch(false))
+ });
cx.spawn_in(window, async move |this, cx| {
let mut all_branches = all_branches_request
diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs
index 2207b8c599ab34bec501485767bc342f01553bb8..ec06f6776cf9dddc710558810686e54aa1aa899e 100644
--- a/crates/git_ui/src/project_diff.rs
+++ b/crates/git_ui/src/project_diff.rs
@@ -199,7 +199,7 @@ impl ProjectDiff {
let Some(repo) = project.read(cx).git_store().read(cx).active_repository() else {
return Task::ready(Err(anyhow!("No active repository")));
};
- let main_branch = repo.update(cx, |repo, _| repo.default_branch());
+ let main_branch = repo.update(cx, |repo, _| repo.default_branch(true));
window.spawn(cx, async move |cx| {
let main_branch = main_branch
.await??
diff --git a/crates/git_ui/src/worktree_picker.rs b/crates/git_ui/src/worktree_picker.rs
index 6e58de1ae141bd3770b57b93c4a89b9ea2af56bb..3d71ff4abc254a689f72e81d388b30f2aa2b3873 100644
--- a/crates/git_ui/src/worktree_picker.rs
+++ b/crates/git_ui/src/worktree_picker.rs
@@ -60,9 +60,9 @@ impl WorktreeList {
.clone()
.map(|repository| repository.update(cx, |repository, _| repository.worktrees()));
- let default_branch_request = repository
- .clone()
- .map(|repository| repository.update(cx, |repository, _| repository.default_branch()));
+ let default_branch_request = repository.clone().map(|repository| {
+ repository.update(cx, |repository, _| repository.default_branch(false))
+ });
cx.spawn_in(window, async move |this, cx| {
let all_worktrees = all_worktrees_request
diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs
index c0b5862b0b12cdb94a5d6f885e96f85544469596..b1ee419a0d822fe9a1194121c78b7b1c5c828aac 100644
--- a/crates/project/src/git_store.rs
+++ b/crates/project/src/git_store.rs
@@ -2229,7 +2229,7 @@ impl GitStore {
let branch = repository_handle
.update(&mut cx, |repository_handle, _| {
- repository_handle.default_branch()
+ repository_handle.default_branch(false)
})
.await??
.map(Into::into);
@@ -5189,12 +5189,15 @@ impl Repository {
)
}
- pub fn default_branch(&mut self) -> oneshot::Receiver>> {
+ pub fn default_branch(
+ &mut self,
+ include_remote_name: bool,
+ ) -> oneshot::Receiver>> {
let id = self.id;
self.send_job(None, move |repo, _| async move {
match repo {
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
- backend.default_branch().await
+ backend.default_branch(include_remote_name).await
}
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
let response = client