2e2fb0a21811ce2dbaca5bb738b0be8f94de051e
372
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ac5538b723 |
Fix several small performance inefficiencies in hot paths (#61275)
# Objective Land five small, independent performance fixes found during an audit of hot paths (anchor resolution, line shaping, worktree scanning, and sorting in multibuffers). ## Solution Each fix is its own commit, so **this PR is best reviewed commit-by-commit** — every commit message contains the full reasoning for that change: - `text`: Avoid redundant rope traversal in `Anchor → usize` conversion — call `offset_for_anchor` directly instead of `summary_for_anchor`, which recomputed the same byte offset with ~4 extra O(log n) tree walks. This is the hottest anchor-resolution path. - `editor`: Avoid double allocation per shaped line — `line.as_str().into()` instead of `line.clone().into()`, which allocated twice per visible line, every frame. - `text`: Use `sort_unstable_by_key` in operation queue insertion — Lamport timestamps are unique keys and duplicates are deduped right after, so stability buys nothing. - `multi_buffer`: Sort with an explicit comparator instead of `sort_unstable_by_key`, which cloned a `PathKey` (`Arc` refcount bump) on every comparison. - `worktree`: Replace O(n²) `Vec::remove` in the deferred-directory pass with an O(1) `None` assignment — the vec is already `Vec<Option<ScanJob>>` and is consumed with `.flatten()`. ## Testing - No behavior changes intended; all changes are mechanical and tests affected crates pass: `text`, `editor`, `multi_buffer`, `worktree`. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Improved editor performance through several micro-optimizations in anchor resolution, line shaping, and worktree scanning. |
||
|
|
b562439e93 |
project_panel: Add remote support for undo/redo system (#59709)
# Objective Add remote (SSH) and collaboration support for trashing and restoring files in the project panel, which in turn enables undo/redo of trash operations against remote and collab projects. Relates to #5039. ## Solution - Updated the project panel undo system to carry `TrashId` instead of `TrashedEntry`. - Using `TrashedEntry` could get hairy, as it includes paths, which wouldn't play too nicely when using, for exapmle, macOS as the client and Windows as the host. Using a simple identifier is much easier in this regard and simplifies implementation. - Enabled the Trash action and context-menu entry on remote projects, and removed the command palette filter in `ProjectPanel::new` that was still hiding the action on remote. - As far as I can tell, there isn't a reliable way to detect whether a given remote actually supports the OS trash, so we expose the action everywhere rather than guessing. On a remote without trash support the action will fail when invoked but this is a conscious tradeoff until we find a better way to handle this. - `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on each `Fs` implementation. Trashed files are referenced by an opaque `TrashId` instead of passing a `TrashedEntry` around, which avoids serializing filesystem paths in remote messages. - Split the old `delete_entry(trash: bool)` API into distinct `trash_entry`/`trash_file` (returning a `TrashId`) and `delete_entry`/`delete_file` across `Project`, `Worktree`, `LocalWorktree` and `RemoteWorktree`. - This lets us drop the optional trash result (`Option<TrashedEntry>`) from the delete path and require a `TrashId` from the trash path. - Added new proto messages (`TrashProjectEntry`, `TrashProjectEntryResponse`, `RestoreProjectEntry`, `RestoreProjectEntryResponse`) to let clients request the host to trash or restore entries. - This deprecates `DeleteProjectEntry::use_trash`, but the host still honors it. An older collab peer may request trashing via that flag instead of the newer `TrashProjectEntry`. If the host ignored it, a newer host would permanently delete a file the user meant to send to the trash. The field will be removed in a later PR once all supported peers use `TrashProjectEntry`. ## Testing The following tests were introduced to ensure the new behavior is correctly tested: * `remote_server::remote_editing_tests::test_remote_trash_restore` – Tests trashing a project entry in remote * `remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash` – Test to ensure we continue respecting `DeleteProjectEntry::use_trash` until it is fully removed * `project_panel::tests::undo::trash_directory_undo_redo` – Not related to these changes but a nice to have as we were missing a test ensuring that trashing and then undoing and redoing it for a directory works as expected Besides these, the following scenarios were manually tested against a remote session on the same machine (macOS): - Trashing → Undo (Restore) → Redo (Trashing) - Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing) - Rename → Undo (Rename) → Redo (Rename) - Move → Undo (Move) → Redo (Move) - Batch Move → Undo (Batch Move) → Redo (Batch Move) ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] 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) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Yara <git@yara.blue> |
||
|
|
f181a2f47b |
Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as well as better sharing for our projects. This also includes the WIP AbsPath abstraction as well as some bug fixes from internal tooling. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
e9d6cd2f05 |
Open gitignored subdirectories as their own workspace (#60918)
# Objective Let's git ignored directories be opened as their own project ## Solution - Add an "ignored dir" carve out when checking project collision ## Testing - Added tests ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [-] Unsafe blocks (if any) have justifying comments - [-] 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) - [x] Tests cover the new/changed behavior - [-] Performance impact has been considered and is acceptable --- Release Notes: - |
||
|
|
55242071f1 |
search: Fix ignored file search in nested Git directories (#60772)
# Objective - Fix ignored file search in nested Git directories (even just empty `.git` directories, not real git repositories) - Fixes #52328 ## Solution Keep parent ignore rules when rebuilding a nested repository's ignore stack. This hides root-ignored files again after excluded-file search is turned off, without breaking global ignore handling. For example: ```ascii project/ ├── .git/ ├── .gitignore # log/ ├── app/ │ └── a.txt # hello └── log/ ├── .git/ # nested repository marker └── b.txt # hello ``` Both `a.txt` and `b.txt` have string `hello`. Initially, searching for `hello` returns only `a.txt`. Enabling `Also search files ignored by configuration` rescans `log/` and returns both files. Before this change, disabling the option still returned both files because the rescan dropped the parent `.gitignore` and marked `b.txt` as included. ## Testing - Did you test these changes? If so, how? To test changes in this pull request create a simple project with the following dir structure: ``` .git .gitignore a/a.txt b/b.txt b/.git # this is an empty directory ``` Add `b` folder as an ignore entry to the `.gitignore` file: `b/` - Are there any parts that need more testing? No - How can other people (reviewers) test your changes? Is there anything specific they need to know? Use the test scenario above - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? macOS ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase By using the structure above here is how it works before changes here: https://github.com/user-attachments/assets/2028123e-388e-4390-b131-ddf4642ab5f3 Notice that `hello` string from file `b.txt` is listed in search results regardless of the option `Include excluded files` And here is after: https://github.com/user-attachments/assets/99c627a2-e16a-4fdd-979b-2e0bacbd827d Notice that `hello` string from file `b.txt` is **not** listed in search results when the option `Include excluded files` is disabled. Release Notes: - Fixed ignored file search in nested Git directories |
||
|
|
2b9b3c7ea2 |
worktree: Watch .git/refs subdirectories for external ref updates (#60660)
On Linux and FreeBSD the native file watcher is non-recursive, so a watch on the .git directory itself does not report changes to files nested below it. Loose refs live in nested directories under refs, so external git commit, fetch, branch, and update-ref operations that don't also touch a direct child of .git (like the index) went entirely unnoticed. Watch every directory in the refs tree when a repository is inserted, and watch directories subsequently created under refs (new remotes, slash-named branches) as their creation events arrive. On platforms with recursive watchers these registrations dedupe against the existing recursive watch, making them free. --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
60314a7416 |
Open non-writeable files in Capability::Read mode (#57202)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #57174 Release Notes: - Open non-writeable files in Capability::Read mode Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
11d216d8bf |
worktree: Refresh all repositories sharing a changed git directory (#60664)
update_git_repositories mapped a changed .git path to a repository with find_map, so when several repositories share a git directory - a main checkout plus one of its linked worktrees in the same project worktree - a ref update under the shared common dir only bumped git_dir_scan_id on whichever repository iterated first, leaving the others stale until an unrelated event happened to refresh them. Collect every matching repository and bump each one. --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
7f5cf583dc |
Fix worktree entry IDs for symlinked files (#57846)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #55792 Release Notes: - Fixed files in pnpm workspaces moving to symlinked `node_modules` paths after saving. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
ea87b05794 |
Fix worktree grouping for bare checkouts (#59968)
Summary - Track whether a worktree root is itself a linked Git worktree. - Use that metadata when computing project group keys so bare checkout worktrees group under the repository identity path. - Propagate the metadata through remote worktree protocols and add local/remote regression coverage. Background Bare checkout layouts can place linked worktrees under the repository identity directory, e.g. `/monty/.bare` with worktrees like `/monty/feature-a`. We were treating those linked worktree paths as separate project identities, which caused the sidebar to move agent threads under the active worktree instead of the shared repository group. We also exclude adding this to collab intentionally, we can open a different PR for that if we need to. Closes #59910 Closes AI-431 Test Plan - `cargo fmt --package project --package worktree --package remote_server --package workspace --package collab --package proto` - `git --no-pager diff --check` - `cargo test -p project test_project_group_key -- --nocapture` - `cargo test -p remote_server test_remote_root_repo_common_dir -- --nocapture` - `cargo test -p worktree remote_worktree -- --nocapture` - `cargo test -p workspace test_remote_project_root_dir_changes_update_groups -- --nocapture` - `cargo check -p collab` Self-Review Checklist: I've reviewed my own diff for quality, security, and reliability Unsafe blocks (if any) have justifying comments The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Tests cover the new/changed behavior Performance impact has been considered and is acceptable Release Notes: - Fixed agent thread/sidebar grouping for Git worktrees backed by bare checkouts. --------- Co-authored-by: Anthony Eid <anthony@zed.dev> |
||
|
|
cd7f1a0fb1 |
worktree: Avoid dropping git repositories during watcher rescans (#59976)
Related to #59610. # Objective When the filesystem watcher loses sync (common on macOS under heavy churn, e.g. a dev build constantly rewriting `target/` and `.git/`), it forces a recursive rescan of the worktree root. That rescan removes the whole subtree from the snapshot before re-scanning it, and `remove_path_from_snapshot` was unconditionally pruning the worktree's git repositories along with it. Because the scanner publishes intermediate snapshots while it works, a snapshot could be published in the window after the repository was pruned but before `.git` was re-scanned. The `GitStore` would see the repository as removed, tear it down, and then re-create it with a fresh `RepositoryId` once it reappeared — churning the id on every watcher overflow and spamming `RepositoryUpdated` events to all consumers (git panel, git graph, blame, etc.). Here some debug logs, **See** that the **repoId** inside the git graph event is different and is **8** now, even though I only have one repository and didn't do any git related updates. ``` 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for Native; scheduling rescans for 11 registrations 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more 2026-06-26T18:38:44+02:00 INFO [git::repository] opening git repository at "/Users/remcosmits/Documents/code/zed/.git" using git binary "/opt/homebrew/bin/git" 2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled 2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled 2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled 2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled 2026-06-26T18:38:44+02:00 ERROR [crates/zed/src/main.rs:1991] Is a directory (os error 21) 2026-06-26T18:38:44+02:00 INFO [project::prettier_store] Prettier config file ".prettierrc" changed, reloading prettier instances for worktree 1 2026-06-26T18:38:45+02:00 INFO [project::prettier_store] Prettier config file ".prettierrc" changed, reloading prettier instances for worktree 1 2026-06-26T18:38:45+02:00 INFO [git::repository] opening git repository at "/Users/remcosmits/Documents/code/zed/.git" using git binary "/opt/homebrew/bin/git" 2026-06-26T18:38:45+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled 2026-06-26T18:38:45+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled [crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId( 1, ) [crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id" [crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId( 8, ) [crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId( 1, ) [crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id" [crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId( 8, ) [crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId( 1, ) [crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id" [crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId( 8, ) [crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId( 1, ) ``` ## Solution This change makes `remove_path_from_snapshot` prune git repositories only when the path was genuinely removed (`metadata == Ok(None)`), not during a recursive refresh where the subtree is about to be re-scanned. Stale repositories are still reaped authoritatively against the filesystem in `update_git_repositories`. ## Testing It seems to be that I'm the only that can reproduce this issue @Anthony-Eid tried reproducing this but couldn't. **Note**: that I dindn't write a regression test since this is a async timing issue that I couldn't figure out to write a test for. Steps how I can reproduce this: 1. Run `cargo run` 2. Open `Zed` as only project 3. Open the git graph 4. Open a rust file e.g. `git_graph.rs` 5. CTRL-C inside the terminal to kill Zed 6. Run `cargo run` see that you have git graph open and a rust file, note that cargo check is doing it's thing. 7. See within a few seconds that Zed's UI flickers (especially the project panel & nav bar with the selected repo & branch) ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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 ## Showcase **Note**: This PR only fixes that we don't send as many git store events, so this results in not having to update the nav bar where the selected repo & branch on every event (that is duplicated in a way). Result of that we now also fixed a rare case that the git graph commit entries where gone, this was because we incremented the **repoId** and the graph couldn't find the repo anymore with the id that it has stored on the gitGraph struct itself. **Before** See that the nav bar with selected repo & branch flickers and the git graph commits are empty. https://github.com/user-attachments/assets/0957be7f-e732-4fd0-a950-29c496b6407d See that the git panel entries flicker and we have an temporary empty panel. https://github.com/user-attachments/assets/3b70e45c-c720-4009-aefa-696986d731b4 **After** See that the nav bar with selected repo & branch does not flicker anymore and that the git graph keeps showing the commits. https://github.com/user-attachments/assets/6e8a74f6-4a8e-4b28-ad30-574c2f33c6d9 See that the git panel does not show an empy state and does not flicker anymore. https://github.com/user-attachments/assets/1df7aacf-b337-4b81-b195-e67d8554d9da --- cc @Anthony-Eid Since we where debugging this yesterday. Release Notes: - Fixed git repositories being repeatedly torn down and re-created when the filesystem watcher forced a rescan, which caused redundant git status refreshes and UI churn. |
||
|
|
6febe1c45a |
Allow file watchers to handle case insensitive file systems (#59579)
## Goal This PR fixes a bug in our file system watcher. Because it matched paths case sensitively, it didn't account for file systems that are case insensitive by default (macOS, Windows, etc.). As a result, when multiple subscribers watched the same path using different casing, Zed could fail to emit file system events to some of them. ### Reproduction I reproduced this with the `tsgo` LSP, which lowercases the file path of the visible worktree root it runs in. Zed subscribes to worktree roots to receive FS updates — e.g. external edits to a buffer, or git state changes — but it doesn't normalize the path when subscribing, so the two casings never matched. ### Fix Fixing this took longer than expected, because I spent a while deciding on an approach. I considered three: - **Follow VS Code's lead:** always treat macOS/Windows as case insensitive and Linux as case sensitive. Simple, but it would leave a couple of bugs. - **Real case the path at registration:** walk each component and use syscalls to rewrite it to match what the file system shows the user (e.g. Finder shows `/Project/some`, so a request to watch `/project/some` becomes `/Project/some`). This had rough edge cases with symlinks that I didn't want to deal with. - **Detect via syscalls whether a path is on a case-insensitive (normalizing) file system and match accordingly** — what I went with. The one wrinkle is that Windows can mark individual directories case-sensitive… because Windows. I also added integration tests to prevent future regressions. Note: Windows currently defaults to case insensitive; implementing the actual case sensitivity check for it is left to a follow up PR. Helps #38109 #35861 #52376 and maybe #41195 ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed missed file system events on case-insensitive filesystems that could cause stale git state and other sync issues --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
45afbac0a5 |
Fix project grouping for Git repo subdirectories (#57998)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - No unsafe blocks added. - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - No new UI components; behavior better matches user intent for opened folders. - [x] Tests cover the new/changed behavior - Added sibling-subdirectory project group regression coverage. - Extended subfolder Git-status test to ensure Git still uses the parent repo while project identity stays at the opened folder. - [x] Performance impact has been considered and is acceptable - Change adds one small enum/optional field check when computing worktree paths; no meaningful performance impact expected. Closes #57997 Release Notes: - Fixed project grouping for opened subdirectories that share the same parent Git repository. ## Screenshots ### Before <img width="334" height="224" alt="Screenshot 2026-05-28 at 5 29 55 PM" src="https://github.com/user-attachments/assets/ad5b13c5-11a7-4fa4-a074-a519521641da" /> <img width="485" height="201" alt="Screenshot 2026-05-28 at 5 28 36 PM" src="https://github.com/user-attachments/assets/9cd2c138-92f5-4ff2-bf8f-21cdbeff0b79" /> ### After <img width="343" height="253" alt="Screenshot 2026-05-28 at 5 29 40 PM" src="https://github.com/user-attachments/assets/56a4dca7-f0a1-4530-8c89-3c4773b90486" /> <img width="420" height="206" alt="Screenshot 2026-05-28 at 5 29 22 PM" src="https://github.com/user-attachments/assets/d118340a-955e-4483-9888-4b566f59b931" /> --------- Co-authored-by: Anthony Eid <anthony@zed.dev> Co-authored-by: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com> |
||
|
|
36a3a2a784 |
Include changed paths in remote UpdatedEntries events (#58157)
Include the changed paths in `UpdatedEntries` events emitted by remote worktrees. Previously a remote worktree emitted `Event::UpdatedEntries` with an empty changeset (`Arc::default()`), discarding the changed paths. The changeset now carries the real added/updated/removed paths, resolving removed entries against the previous snapshot. Release Notes: - Fixed file change events not reporting changed paths in remote projects |
||
|
|
2408640e5f |
git: Avoid unnecessary git repo rescans when unrelated git files change (#59318)
# Objective
Zed triggers many git rescans whenever an outside git command modifies
files inside `.git` dir. This becomes especially problematic when
working on large repos or doing remote development on machines with slow
filesystems.
## Solution
Events for object writes, hook files, lock files, and the reflogs of
HEAD/branches/remote-tracking branches carry no git changes that Zed
cares about beyond what the ref or events already cover. So changes to
these files shouldn't trigger a full git rescan.
## Testing
I extended the existing unittests to add the additionally ignored
directories and lock files.
I also manually verified the changes by viewing the zed git debug logs
that get generated when running `git gc` on a freshly gc'ed repo.
Previously Zed triggered **7 worktree updates**, with these changes it
was reduced to **a single worktree update** which is due to
`.git/packed-refs` which we can't ignore.
**main:**
```
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:52+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:52+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:53+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:53+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:54+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:54+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:54+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:54+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:55+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:55+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:55+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:55+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-13T13:56:55+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-13T13:56:56+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-13T13:56:56+01:00 DEBUG [project::git_store] fetched remotes
2026-06-13T13:56:56+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-13T13:56:56+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-13T13:56:56+01:00 DEBUG [project::git_store] load merge details
2026-06-13T13:56:56+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }
```
**This PR:**
```
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] received worktree update for repositories: [UpdatedGitRepository { work_directory_id: ProjectEntryId(0), old_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), new_work_directory_abs_path: Some("/Users/lgeiger/code/zed"), dot_git_abs_path: Some("/Users/lgeiger/code/zed/.git"), repository_dir_abs_path: Some("/Users/lgeiger/code/zed/.git"), common_dir_abs_path: Some("/Users/lgeiger/code/zed/.git") }]
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] local worktree repos changed
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] run scheduled git status scan
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] starting compute snapshot
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] fetched branches, head commit, worktrees
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] fetched remotes
2026-06-15T01:19:51+01:00 DEBUG [git::repository] Checking for git status in [""]
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] fetched statuses, diff stats, stash entries
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] load merge details
2026-06-15T01:19:51+01:00 DEBUG [project::git_store] new merge details: MergeDetails { merge_heads_by_conflicted_path: {}, message: None }```
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] 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)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Reduced number of git operations when repository state changes outside of zed
|
||
|
|
2252cad9b9 |
git: Fix .git directory being removed from watcher when excluded via file_scan_exclusions (#57895)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #57888 Commit `c19cc4c51e0f64eec42168943050f2deeccaa076` introduced the bug on #50412 . This change modified the `remove_path` function to also remove the path from the watcher: https://github.com/zed-industries/zed/blob/f0341c96a17630e5dfb441701441945dbb2a605a/crates/worktree/src/worktree.rs#L3218-L3220 And when a user modified the global setting and doesn't include `.git` in it, like: ```jsonc // ~/.config/zed/settings.json { "file_scan_exclusions": ["foo"] } ``` But then includes it on their local project settings: ```jsonc // ~/my/local/project/.zed/settings.json { "file_scan_exclusions": ["**/.git"] } ``` It causes zed to stop watching for changes on `.git` Release Notes: - Fixed bug where zed stopped watching change on `.git` directory if it was added to the project local `file_scan_exclusions` |
||
|
|
b6c7496aea |
multi_buffer: Don't eagerly clone BufferSnapshot in range_to_buffer_ranges (#59190)
Both cloning and dropping of these has quite a bit of overhead (despite them being snapshots), so avoid where possible, especially in display map syncing Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
cafbf4b5df |
Improve didChangeWatchedFiles handler performance (#59078)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] 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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: John Tur <john-tur@outlook.com> Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
300fde7b70 |
Fix stale Git UI on reftable repositories on Linux (#58719)
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 is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable The non-recursive Linux watcher causes subdirectories inside `.git/` to need explicit watching. For repos using the reftable backend, all ref data lives in `.git/reftable/`. Operations in the UI like uncommitting and creating new branches only modify refs inside `.git/reftable` without changing `.git/HEAD`'s contents, so no inotify event fires on `.git/` itself and the git UI never refreshes. Essentially all this change does is check if `.git/reftable/` exists and if it does, add it to the watcher. On the recursive macOS/Windows watchers, `FsWatcher::add` returns early safely so this *should* not be a performance loss apart from the brief lookup. Release Notes: - Fixed Git UI not refreshing on Linux for repositories using the reftable backend. |
||
|
|
0c660d0cb4 |
worktree: Don't eagerly remove watchers (#58692)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
693356221e |
Fix symlinked root rename event handling (#58624)
Fixes #58619. When a worktree is opened through a symlinked root, filesystem events can arrive rooted at either the opened symlink path or the canonical target path. The worktree event processing path already handled canonical target paths, but dropped events reported under the opened symlink root because it only derived relative paths from the canonical root. This PR adds a regression test that opens `/link -> /target`, renames `/link/subdir-a` to `/link/subdir-aa`, and verifies `subdir-aa/config.ini` remains visible in the worktree. The production fix accepts event paths under the opened worktree root as a fallback when deriving relative paths. Testing: - Verified the new regression test fails with only the test commit applied. - `cargo test -p worktree --test integration test_renaming_subdir_under_symlinked_root_keeps_children` - `cargo test -p worktree --test integration test_symlinks_pointing_outside` |
||
|
|
590aaafcab |
Symlink scan option (#53646)
Adds option to always search/scan symlinks for more thorough coverage of #41887 issue This is a significant rewrite and more thoroughly reviewed and tested version of the old PR here: https://github.com/zed-industries/zed/pull/46344 The "never" option was removed from the old PR, since "expanded" is now the default. Perhaps "never" can be added as an option later if there's demand for it. Adding that option may resolve #48890 for instance. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable (significant impact with "always" option, but "expanded" is default) Release Notes: - Add option choose between including expanded symlinks or include all symlinks in project search (#41887) --------- Co-authored-by: Eric Holk <eric@zed.dev> |
||
|
|
954ee58c15 |
worktree: Fix linked worktree git dir event handling (#57782)
When a linked worktree receives fs events for its .git directory, the event path resolves to the per-worktree git dir (e.g. main_repo/.git/worktrees/<name>), not the .git directory itself or the common dir. The existing match only checked common_dir_abs_path and repository_dir_abs_path, causing the repository entry to be missed and then removed as stale. Add dot_git_abs_path to the match, and fix the staleness metadata check to use dot_git_abs_path (the actual .git entry) rather than common_dir_abs_path (which may be outside the worktree root). Extracted from https://github.com/zed-industries/zed/pull/53453 Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Kieran Freitag <kfreitag@kieran.ca> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
315d474c2e |
Honor anchored patterns in .git/info/exclude (#57779)
Patterns in `.git/info/exclude` that contain a slash (e.g. `.claude/worktrees`) are anchored: Git matches them relative to the project root. Zed was instead matching them relative to the `.git/info/` directory that the file lives in, so they matched nothing and had no effect. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Support anchored patterns in .git/info/exclude --------- Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
4129fc87d8 |
Fix the filtering of index.lock + COMMIT_MESSAGE FS events to work in linked worktrees (#57763)
Zed reloads a lot of data about a git repository any time any file changes inside of the `.git` directory, with the exception of a few known paths that we know do not warrant a reload, such as `index.lock` and `COMMIT_MESSAGE`. Previously, we ignored FS events for those files, but we used a specific path that only worked for the main worktree. This caused a lot of unnecessary reloads when using linked worktrees. Now we ignore those files in a general way, by their filename, so that the optimization applies to linked worktrees as well. @cole-miller Noticed this bug. Release Notes: - Fixed unnecessary reloading of Git state that could occur when editing in linked worktrees. |
||
|
|
bcfbf669bd |
git: Degrade gracefully when refreshing git state (#57292)
This PR changes the git store's `compute_snapshot`, which runs to update state that depends on the contents of `.git`, to degrade gracefully when fetching individual pieces of state fails. For example, when fetching the list of branches fails, instead of returning early from the function (leaving the previous git state snapshot in place with stale state), we continue with an empty list of branches. This prevents failures of individual git commands from making the entire git UI get stuck indefinitely. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - Fixed an issue where failing to fetch branches using the git CLI would prevent other git-related state from being updated. |
||
|
|
57a64fc824 |
Add some more logging to diagnose lost FS events and stale git state (#57173)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
5e62281357 |
fs: Defer initializing poll watcher until after initial worktree scan (#56207)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #56021 Closes #56100 Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
fe1f7a60e4 |
Skip Git tracking for invisible worktrees (#55760)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
42f9420437 |
Fix handling of git repositories with an external git directory (#55402)
Closes https://github.com/zed-industries/zed/issues/54824 Previously, we always assumed that `gitdir` was an absolute path. Also, we did not correctly handle custom gitignore files that were configured via separate git directories. Release Notes: - Fixed failure to recognize git repositories where `gitdir` was expressed as a relative path. - Fixed handling of gitignores in git repositories that use a separate git dir. |
||
|
|
99c67b8d15 |
Respect .git/info/exclude in secondary worktrees (#51536)
Closes #50880 When a git worktree linked via a `.git` file (e.g. `gitdir: /repo/.git/worktrees/my-worktree`) was opened in Zed, entries in `.git/info/exclude` were not respected. This is now fixed. Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - Fixed `.git/info/exclude` not being respected when opening a secondary git worktree https://github.com/user-attachments/assets/f38df5dc-96eb-40a8-a77c-0932a2c8575b --------- Co-authored-by: Lukas Wirth <lukas@zed.dev> Co-authored-by: Lukas Wirth <me@lukaswirth.dev> |
||
|
|
320888142f |
Rust 1.95 (#55104)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A |
||
|
|
c5a2807492 |
Remove smol as a dependency from a bunch of crates (#53603)
We aren't making use of it in these crates and it unblocks some web-related work Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
9b40411c6a |
Fix bad GitHub merge queue merge (#54721)
No, sadly, the title is not a typo. See https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context. I'll read with joy and popcorn through that root cause analysis. It makes literally zero sense what happened here, but for some completly bonkers reason GitHub completely messed up the merge queue with https://github.com/zed-industries/zed/pull/54632. I have no idea how it happened. It makes literally zero sense. A PR going into the merge queue should have the same LoC when getting out of it. GitHub obviously does not check this. GitHub causes extra work with a feature that is supposed to save time. Thanks, I guess. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
0ab64d6414 |
branch_picker: Add button to filter remote branches (#54632)
This PR brings back the button to filter remote branches when accessing the title bar's branch picker with the mouse. It was unintentionally removed when we introduced the new worktree picker. Release Notes: - N/A |
||
|
|
109c2238aa |
Fix freeze when binary content written to open empty file (#53074)
Opening an empty file then writing binary content to it on disk causes a permanent freeze (not a crash — requires force kill). `reload_impl` loads raw bytes via `load_bytes` and decodes with `encoding_rs` but never checks if the content is binary. The existing binary check in `decode_file_text` only runs on fresh opens, not reloads. So binary content enters the buffer as lossy UTF-8. Binary data has almost no newlines, producing a single enormous row. The wrap map's sync fast path in `flush_edits` checks row count (< 100 rows) but not line length, so it runs `wrap_line` on a multi-MB single line synchronously on the main thread via `smol::block_on`. Font shaping millions of non-ASCII replacement characters blocks the UI thread indefinitely. Two fixes, both one-condition guards: - Null-byte check in `reload_impl` before decoding, same heuristic (first 8000 bytes) used by `decode_file_text` on fresh opens. Binary content never enters the buffer. - Column-length guard (`MAX_SYNC_WRAP_COLUMNS = 10_000`) on the wrap map sync fast path so absurdly long lines fall through to the async background path. Defense in depth for any single-line content that's too long to shape synchronously. ## Test plan - [ ] Open an empty file in Zed, write binary content to it externally (e.g. `cp /bin/ls /path/to/open-file`) — should show "Binary files are not supported" instead of freezing - [ ] Open a compressed file (zip, gz) that starts empty and gets filled — same behavior - [ ] Normal text file reload still works (no regression) - [ ] Very long single-line text files (>10k columns) don't freeze the editor --------- Co-authored-by: deadcode-walker <268043493+deadcode-walker@users.noreply.github.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
cd944ad8d6 |
git: Correctly filter ignored paths inside gitdirs on all platforms (#54548)
Previously the worktree's handling of events inside gitdirs looked like this: - First, deduplicate paths for which we received events by dropping any path that is a suffix of another one - Then, filter out events for deliberately ignored paths like `.git/index.lock` - Finally, collect a set of affected gitdirs based on the paths that remain This doesn't work on Windows, because when events occur for paths inside `.git` we also get an event for `.git` itself. The deduplication steps drops `.git/index.lock` before we get the chance to filter it out in the second step. This causes us to rescan git state unnecessarily on Windows. This PR fixes the issue by moving the filtering of ignored paths, and also the handling of `.git/info/exclude`, to just before the deduplication step. We also filter out events for the `.git` directory itself, so that if the batch of events looks like `[".git", ".git/index.lock"]`, we don't trigger `WorktreeUpdatedGitRepositories`. Self-Review Checklist: - [ ] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
dfb8e3451c |
settings: Remove the project_name project setting (#54511)
The `project_name` worktree setting was added in #36713 to let users override the name shown in the window title. Its description ("The displayed name of this project. If left empty, the root directory name will be displayed.") suggests broader coverage, and #46440 reports the reasonable expectation that it should also apply in the project switcher. In practice the setting has only ever affected `Workspace::update_window_title`, so everywhere else (recent projects, the multi-worktree pane, ...) keeps falling back to the worktree root name. Rather than plumb the setting through each of those surfaces, I'm removing it. Having a project-level setting control how your editor displays the project has downsides. For example it means a checkout can dictate UI in someone else's Zed. The natural home for a custom display name is the workspace DB, set from the UI, which is what we should do if we want this feature back. If you want this back, the path forward is to store the display name in `WorkspaceDb`, expose a UI affordance to edit it, and read it from `update_window_title`, `recent_projects::get_recent_projects` / `get_open_folders`, and any other places that currently derive a display name from the worktree root. Closes #46440 Release Notes: - Removed the `project_name` project setting. It only ever affected the OS window title, and the expectation that it would show up in the project switcher and elsewhere is better served by a future UI-driven, per-workspace setting stored locally. |
||
|
|
57d9e1f441 |
git: Revert skipping of events for the .git directory itself (#54443)
This reverts #54329 and the part of #52499 that was an earlier attempt at the same thing, which caused us to incorrectly miss git state updates on Windows. cc @Veykril it seems like we need to find a different way to fix the problem of `.git` scanning cycles. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed a bug causing stale git state on Windows. |
||
|
|
7620b78337 |
Fix zed irresponsive on symlinked directory events outside the editor (#50746)
Closes #48729, closes #27263, closes #45954 This PR aims to make zed responsive on symlinked directory events outside the editor. Before you mark this PR as ready for review, make sure that you have: - [ ] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) new-linked-folder is inside /zed-test/zed-project output of ls -ld new-linked-folder `lrwxr-xr-x 1 prayanshchhablani staff 42 28 Mar 23:20 new-linked-folder -> /Users/prayanshchhablani/new-target-folder` this shows new-linked-folder is a symlink folder whose target is new-target-folder which is outside the root dir of the project opened in zed. https://github.com/user-attachments/assets/ffebafc3-2fc4-4293-bdbf-3a894a45e276 Release Notes: - Fixed file watching of symlinks that point outside of the project/watched directory. Zed should now properly respond to changes in files in symlinked directories |
||
|
|
c289ec71a2 |
worktree: Fix crash on rescan of an unregistered linked worktree commondir (#54215)
## Summary Fixes a crash I hit running Zed Preview against a checkout with many linked git worktrees. The panic was: ``` thread '<unnamed>' panicked at crates/worktree/src/worktree.rs:5334:25: update_git_repositories: .git path outside worktree root is not a gitfile: "/Users/eric/repo/zed/.git" ``` The `debug_assert!` in `update_git_repositories` was added in #53443 to catch the case where a `.git` path outside the worktree root is not a gitfile. The comment there explained that a `.git` outside the root should always be a gitfile (as in a linked worktree or submodule), so the assertion was meant to flag "should never happen" paths. But there's a second legitimate case: after a linked worktree's repository has been unregistered from `git_repositories` (for example because its gitfile was removed, or because the filesystem watcher for the common git dir lost sync and rescan-driven cleanup dropped the entry), a subsequent rescan event on the main repo's `.git` directory arrives at the linked worktree's scanner with the common git dir as the `dot_git_dir`. That path: - is outside the linked worktree's root (so it doesn't strip-prefix cleanly), and - is a real directory (not a gitfile), because it's the main repo's `.git`. So the assertion fires, but `continue` is already the right thing to do — there's simply nothing left for this scanner to do with a path that isn't its repository anymore. On macOS, the trigger in practice is the FSEvents API setting `MustScanSubDirs` / `UserDropped` / `KernelDropped` on an event (which `notify` surfaces as `need_rescan()`), which our `FsWatcher` converts into a `PathEvent { path: <watched path>, kind: Rescan }`. Because every linked worktree registers a watcher on the same shared common git dir, one kernel drop fans out into many rescan callbacks, and any one of them hitting a worktree whose repo was just unregistered triggers the panic. ## Changes - `crates/worktree/src/worktree.rs` — drop the `debug_assert!`, broaden the comment to cover both cases. - `crates/worktree/tests/integration/main.rs` — add a failing regression test that drives the exact sequence (repo unregistered, then a Rescan event on the common git dir) and asserts it doesn't panic. The two commits are split so the test commit reproduces the panic on its own, and the fix commit on top makes it pass. Release Notes: - N/A |
||
|
|
0ba60d8a44 |
worktree: Fix .git modified events not being correctly filtered out (#54329)
Release Notes: - N/A or Added/Fixed/Improved ... Co-authored by: Cole Miller <cole@zed.dev> |
||
|
|
6beecae6df |
agent_ui: Improve the new thread worktree UX (#53941)
Closes https://github.com/zed-industries/zed/issues/53262 - Remove the ability to pick a branch from the agent panel; delegate this to the title bar picker - Make the worktree creation earger, just as you selected whether you want to create it from main or current branch - Remove flicker when creating a new worktree and switching to a previously existing one - Improve some UI stuff: how we display that a worktree is creating/loading, the branch and worktree icons, etc. - Fixed a bug where worktrees in a detached HEAD state wouldn't show up in the worktree pickers A big part of the diff of this PR is the removal of everything involved with the `StartThreadIn` enum/the set up involved in only creating the worktree by the time of the first prompt send. Release Notes: - Agent: Improved and simplified the UX of creating threads in Git worktrees. - Git: Fixed a bug where worktrees in a detached HEAD state wouldn't show up in the worktree picker. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev> |
||
|
|
55e47138e9 |
agent_ui: Unify draft and background threads into retained threads (#53737)
> **Foundation PRs:** #53732, #53733, #53734 — These three draft PRs contain the base refactors (retained workspaces, agent panel overlay split, ThreadId introduction) that this PR builds on. ## Goal Remove `DraftId` and merge `draft_threads` + `background_threads` into a single `retained_threads: HashMap<ThreadId, Entity<ConversationView>>` in `AgentPanel`. A draft is just a thread that hasn't sent its first message — no separate identity or storage needed. ## Changes ### agent_panel.rs - Remove `DraftId` / `DraftIdCounter` / the `Global` impl - Merge the two maps into `retained_threads` - Add `thread_id: ThreadId` field to `BaseView::AgentThread` - Rename methods: `create_draft` → `create_thread`, `activate_draft` → `activate_retained_thread`, `remove_draft` → `remove_thread`, etc. - Replace `clear_active_thread` with `show_or_create_empty_draft` - Update `update_thread_work_dirs` to sync `ThreadMetadataStore` paths when worktrees change - Keep `load_agent_thread(...)` cleanup so activating a real thread removes empty retained drafts ### sidebar.rs - Remove `active_entry` derivation from `rebuild_contents` (was racing with deferred effects) - Add `sync_active_entry_from_panel` called from event handlers instead - Simplify `ActiveEntry` — remove `ThreadActivation` struct, make `session_id` optional - Move `seen_thread_ids` to global scope (was per-group, causing duplicate thread entries) - Remove dead code: `clear_draft`, `render_draft_thread` - Generalize `pending_remote_thread_activation` into `pending_thread_activation` so all persisted-thread activations suppress fallback draft reconciliation - Set the activation guard before local persisted-thread activation switches workspaces - Make `reconcile_groups(...)` bail while a persisted-thread activation is in flight - On `ActiveViewChanged`, clear empty group drafts as soon as a pending persisted-thread activation resolves ### thread_metadata_store.rs - Make `ThreadMetadata.title` an `Option<SharedString>` with `display_title()` fallback - Add `update_worktree_paths` for batched path updates when project worktrees change ### Other crates - Update all `ThreadMetadata` construction sites and title display sites across `agent_ui` and `sidebar` ## Fallback draft invariant This PR now tightens the retained-thread invariant around fallback drafts vs real thread activation/restoration: - Fallback drafts are always empty - User-created drafts worth preserving are non-empty - While a persisted thread is being activated/restored/loaded, sidebar reconciliation must not create an empty fallback draft for that target group/workspace - Once the real thread becomes active, empty fallback drafts in that target group are removed This is enforced by the sidebar-side activation guard plus existing `AgentPanel` empty-draft cleanup after real-thread load. ## Tests Added and/or kept focused sidebar regression coverage for: - `test_confirm_on_historical_thread_in_new_project_group_opens_real_thread` - `test_unarchive_into_inactive_existing_workspace_does_not_leave_active_draft` - `test_unarchive_after_removing_parent_project_group_restores_real_thread` - `test_pending_thread_activation_suppresses_reconcile_draft_creation` Focused test runs: - `cargo test -p sidebar activate_archived_thread -- --nocapture` - `cargo test -p sidebar unarchive -- --nocapture` - `cargo test -p sidebar archive_last_thread_on_linked_worktree -- --nocapture` - `cargo test -p sidebar test_confirm_on_historical_thread_in_new_project_group_opens_real_thread -- --nocapture` - `cargo test -p sidebar test_unarchive_into_inactive_existing_workspace_does_not_leave_active_draft -- --nocapture` - `cargo test -p sidebar test_unarchive_after_removing_parent_project_group_restores_real_thread -- --nocapture` - `cargo test -p sidebar test_pending_thread_activation_suppresses_reconcile_draft_creation -- --nocapture` ## Test fix Fixed flaky `test_backfill_sets_kvp_flag` — added per-App `AppDatabase` isolation in `setup_backfill_test` so backfill tests no longer share a static in-memory DB. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com> Co-authored-by: Mikayla Maki <mikayla@zed.dev> |
||
|
|
e25885bbe6 |
project_panel: Add redo and restore support (#53311)
- Introduce `project_panel::Redo` action - Update all platform keymaps in order to map `redo`/`ctrl-shift-z`/`cmd-shift-z` to the `project_panel::Redo` action ### Restore Entry Support - Update both `Project::delete_entry` and `Worktree::delete_entry` to return the resulting `fs::TrashedEntry` - Introduce both `Project::restore_entry` and `Worktree::restore_entry` to allow restoring an entry in a worktree, given the `fs::TrashedEntry` - Worth pointing out that support for restoring is not yet implemented for remote worktrees, as that will be dealt with in a separate pull request ### Undo Manager - Split `ProjectPanelOperation` into two different enums, `Change` and `Operation` - While thinking through this, we noticed that simply recording the operation that user was performing was not enough, specifically in the case where undoing would restore the file, as in that specific case, we needed the `trash::TrashedEntry` in order to be able to restore, so we actually needed the result of executing the operation. - Having that in mind, we decided to separate the operation (intent) from the change (result), and record the change instead. With the change being recorded, we can easily building the operation that needs to be executed in order to invert that change. - For example, if an user creates a new file, we record the `ProjectPath` where the file was created, so that undoing can be a matter of trashing that file. When undoing, we keep track of the `trash::TrashedEntry` resulting from trashing the originally created file, such that, redoing is a matter of restoring the `trash::TrashedEntry`. - Refer to the documentation in the `project_panel::undo` module for a better breakdown on how this is implemented/handled. - Introduce a task queue for dealing with recording changes, as well as undo and redo requests in a sequential manner - This meant moving some of the details in `UndoManager` to a `project_panel::undo::Inner` implementation, and `UndoManager` now serves as a simple wrapper/client around the inner implementation, simply communicating with it to record changes and handle undo/redo requests - Callers that depend on the `UndoManager` now simply record which changes they wish to track, which are then sent to the undo manager's inner implementation - Same for the undo and redo requests, those are simply sent to the undo manager's inner implementation, which then deals with picking the correct change from the history and executing its inverse operation - Introduce support for tracking restore changes and operations - `project_panel::undo::Change::Restored` – Keeps track that the file/directory associated with the `ProjectPath` was a result of restoring a trashed entry, for which we now that reverting is simply a matter of trashing the path again - `project_panel::undo::Operation::Restore` – Keeps track of both the worktree id and the `TrashedEntry`, from which we can build the original `ProjectPath` where the trashed entry needs to be restored - Move project panel's undo tests to a separate module `project_panel::tests::undo` to avoid growing the `project::project_panel_tests` module into a monolithic test module - Some of the functions in `project::project_panel_tests` were made `pub(crate)` in order for us to be able to call those from `project_panel::tests::undo` ### FS Changes - Refactored the `Fs::trash_file` and `Fs::trash_dir` methods into a single `Fs::trash` method - This can now be done because `RealFs::trash_dir` and `RealFs::trash_file` were simply calling `trash::delete_with_info`, so we can simplify the trait - Tests have also been simplified to reflect this new change, so we no longer need a separate test for trashing a file and trashing a directory - Update `Fs::trash` and `Fs::restore` to be async - On the `RealFs` implementation we're now spawning a thread to perform the trash/restore operation Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Relates to #5039 Release Notes: - N/A --------- Co-authored-by: Yara <git@yara.blue> Co-authored-by: Miguel Raz Guzmán Macedo <miguel@zed.dev> Co-authored-by: Marshall Bowers <git@maxdeviant.com> |
||
|
|
b6562e8afa |
fs: Return trashed file location (#52012)
Update both `Fs::trash_dir` and `Fs::trash_file` to now return the location of the trashed directory or file, as well as adding the `trash-rs` create dependency and updating the `RealFs` implementation for these methods to simply leverage `trash::delete_with_info`. * Add `fs::Fs::TrashedEntry` struct, which allows us to track the original file path and the new path in the OS' trash * Update the `fs::Fs::trash_dir` and `fs::Fs::trash_file` signatures to now return `Result<TrashedEntry>` instead of `Result<()>` * The `options` argument was removed because it was never used by implementations other than the default one, and with this change to the signature type, we no longer have a default implementation, so the `options` argument would no longer make sense * Update `fs::RealFs::trash_dir` and `fs::RealFs::trash_file` implementations to simply delegate to `trash-rs` and convert the result to a `TrashedEntry` * Add `fs::FakeFs::trash` so we can simulate the OS' trash during tests that touch the filesystem * Add `fs::FakeFs::trash_file` implementation to leverage `fs::FakeFs::trash` * Add `fs::FakeFs::trash_dir` implementation to leverage `fs::FakeFs::trash` |
||
|
|
d812adc833 |
sidebar: Better handling for threads in remote workspaces (#53451)
This PR greatly improves our handling of remote threads in the sidebar. One primary issue was that many parts of the sidebar were only looking at a thread's path list and not its remote connection information. The fix here is to use `ProjectGroupKey` more consistently throughout the sidebar which also includes remote connection information. The second major change is to extend the MultiWorkspace with the ability to initiate the creation of remote workspaces when needed. This involved refactoring a lot of our remote workspace creation paths to share a single code path for better consistency. Release Notes: - (Preview only) Fixed remote project threads appearing as a separate local project in the sidebar --------- Co-authored-by: Anthony Eid <anthony@zed.dev> Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> |
||
|
|
fb949ae831 |
Gracefully handle when linked worktree .git path is outside worktree root (#53443)
In `update_git_repositories`, a `.git` path outside the worktree root can occur legitimately when `.git` is a gitfile (as in linked worktrees and submodules) pointing to a directory in the parent repo. Previously this triggered a `debug_panic!`, crashing debug builds. Now we skip the path with a `debug_assert!` that it is indeed a file (not a directory), so a genuine `.git` directory outside the worktree root would still be caught in debug builds. (No release notes because this is extremely hard to encounter until https://github.com/zed-industries/zed/pull/53215 lands) Release Notes: - N/A |
||
|
|
4a5826ba62 |
Make deserialization a bit more resilient to data changes (#53362)
This PR makes sidebar deserialization enforce the invariants that the multiworkspace is supposed to enforce. Also, this PR makes it so that failing to deserialize the active workspace no longer totally fails to deserialize the multiworkspace. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
20f7308677 |
Maintain root repo common dir path as a field on Worktree (#53023)
This enables us to always different git worktrees of the same repo together. Depends on https://github.com/zed-industries/cloud/pull/2220 Release Notes: - N/A --------- Co-authored-by: Eric Holk <eric@zed.dev> |