From 6b95ac3d33ea38b3f4cbfccc83e83233cc132a05 Mon Sep 17 00:00:00 2001 From: Matei Oprea Date: Fri, 18 Sep 2026 10:24:51 +0000 Subject: [PATCH] git: Fix git info exclude action for worktrees (#63967) # Objective - This PR fixes #63760. According to git docs "Patterns read from $GIT_DIR/info/exclude". ## Solution - We were reading the directory's absolute path instead of reading the absolute path to the repository's common .git directory. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed "Add to .git/info/exclude" not working when used from a secondary git worktree --------- Co-authored-by: Smit Barmase --- crates/project/src/git_store.rs | 2 +- .../remote_server/src/remote_editing_tests.rs | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 68ff1f7352..aa539e1840 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -8413,7 +8413,7 @@ impl Repository { is_dir: bool, ) -> oneshot::Receiver> { let id = self.id; - let repository_dir = self.snapshot.repository_dir_abs_path.clone(); + let repository_dir = self.snapshot.common_dir_abs_path.clone(); let path_display = repo_path.as_ref().display(PathStyle::Unix); let path = repo_path.as_unix_str().to_owned(); let file_path_str = if is_dir { diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index 9d897b58cd..dc99eb5df6 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -3702,6 +3702,92 @@ async fn test_add_path_to_git_info_exclude_in_remote_repository( ); } +#[gpui::test] +async fn test_add_path_to_git_info_exclude_in_remote_linked_worktree( + cx: &mut TestAppContext, + server_cx: &mut TestAppContext, +) { + let fs = FakeFs::new(server_cx.executor()); + fs.insert_tree( + "/project", + json!({ + ".git": { + "HEAD": "ref: refs/heads/main", + "config": "[core]\n\tbare = false\n", + "info": { + "exclude": "existing\n", + }, + "worktrees": { + "linked": { + "HEAD": "ref: refs/heads/linked", + "commondir": "../..", + "gitdir": "/worktree/.git", + }, + }, + }, + }), + ) + .await; + fs.insert_tree( + "/worktree", + json!({ + ".git": "gitdir: ../project/.git/worktrees/linked\n", + "logs": { + "app.log": "", + }, + "tmp.txt": "", + }), + ) + .await; + let repository_dir = Path::new("/project/.git/worktrees/linked"); + fs.set_branch_name(repository_dir, Some("linked")); + fs.set_head_for_repo(repository_dir, &[], "head-sha"); + + let (project, _headless) = init_test(&fs, cx, server_cx).await; + project + .update(cx, |project, cx| { + project.find_or_create_worktree(Path::new("/worktree"), true, cx) + }) + .await + .expect("should open remote linked worktree"); + cx.run_until_parked(); + + let repository = project.read_with(cx, |project, cx| { + project + .active_repository(cx) + .expect("remote linked worktree should have an active repository") + }); + + for (path, is_dir) in [("tmp.txt", false), ("logs", true), ("tmp.txt", false)] { + let repo_path = RepoPath::new(path).expect("path should be a valid repo path"); + cx.update(|cx| { + repository.update(cx, |repository, _| { + repository.add_path_to_git_info_exclude(&repo_path, is_dir) + }) + }) + .await + .expect("add to info/exclude request should complete") + .expect("add to info/exclude should succeed for remote linked worktree"); + } + + server_cx.run_until_parked(); + cx.run_until_parked(); + + assert_eq!( + fs.load(Path::new("/project/.git/info/exclude")) + .await + .expect("shared info/exclude should be readable"), + "existing\ntmp.txt\nlogs/\n" + ); + assert!( + fs.metadata(&repository_dir.join("info/exclude")) + .await + .expect("worktree-specific info/exclude metadata should be readable") + .is_none(), + "the action should not create a worktree-specific info/exclude" + ); +} + #[gpui::test] async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let text_2 = "