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