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>
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#56021Closes#56100
Release Notes:
- N/A or Added/Fixed/Improved ...
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.
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>
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 ...
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.
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
## 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
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>
> **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>
- 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>
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
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>
When a single-file worktree's root file no longer exists, the background
scanner would previously enter an infinite retry loop attempting to
canonicalize the path. This caused continuous error logging and resource
waste.
This fix detects when a single-file worktree root cannot be
canonicalized (after attempting the file handle fallback) and emits a
new Deleted event, allowing the worktree to be properly closed.
This is most commonly encountered with temporary files, logs, and
similar files that are opened in Zed and then deleted externally, but
persist in the workspace database across sessions.
Closes#34864
## Test
**Logs** from manual testing:
```
2026-02-17T16:16:11+01:00 INFO [worktree] inserting parent git repo for this worktree: "tmp.md"
2026-02-17T16:16:17+01:00 ERROR [worktree] root path could not be canonicalized: canonicalizing "/Users/***/tmp/tmp.md": No such file or directory (os error 2)
2026-02-17T16:16:17+01:00 INFO [worktree] single-file worktree root "/Users/***/tmp/tmp.md" no longer exists, marking as deleted
2026-02-17T16:16:17+01:00 INFO [worktree] worktree root /Users/***/tmp/tmp.md no longer exists, closing worktree
```
Release Notes:
- Fixed an issue where Zed would enter an infinite retry loop when the
backing file for a single-file worktree was deleted
Closes #ISSUE
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
- [ ] 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)
Release Notes:
- N/A *or* Added/Fixed/Improved ...
- **settings: Do not leak type serialization into downstream crates**
- **worktree: Separate integration tests into separate target**
Release Notes:
- N/A