Skip to content

Wire up worktree archival on thread archive and restoration on unarchive#53215

Merged
rtfeldman merged 43 commits intomainfrom
persist-worktree-3-wiring
Apr 8, 2026
Merged

Wire up worktree archival on thread archive and restoration on unarchive#53215
rtfeldman merged 43 commits intomainfrom
persist-worktree-3-wiring

Conversation

@rtfeldman
Copy link
Copy Markdown
Contributor

Connect the git API and archived worktree data model to the sidebar's archive/unarchive flow:

  • Add thread_worktree_archive module: orchestrates the full archive cycle (WIP commits, DB records, git refs, worktree deletion) and restore cycle (detached worktree creation, reset to recover staged/unstaged state, branch restoration)
  • Integrate into sidebar: archive_thread now persists worktree state before cleanup; activate_archived_thread restores worktrees via git with targeted path replacement for multi-root threads
  • Add pending worktree restore UI state with spinner and cancel button
  • Show toast on restore failure instead of silent log
  • Switch to surviving workspace when archiving active thread whose workspace will be deleted
  • Deserialize persisted project_group_keys on window restore
  • Guard cleanup_empty_workspaces against dropped entities
  • Await rollback DB operations instead of fire-and-forget

Part 3 of 3 in the persist-worktree stack. Stacked on #53214. This wires up parts 1 and 2.

Release Notes:

  • Added worktree state preservation when archiving agent threads, automatically restoring staged and unstaged changes when the thread is unarchived

…ations

Extend the git API with several new capabilities needed for worktree
archival and restoration:

- Add allow_empty flag to CommitOptions for creating WIP marker commits
- Change create_worktree to accept Option<String> branch, enabling
  detached worktree creation when None is passed
- Add head_sha() to read the current HEAD commit hash
- Add update_ref() and delete_ref() for managing git references
- Add stage_all_including_untracked() to stage everything before a
  WIP commit
- Implement all new operations in FakeGitRepository with functional
  commit history tracking, reset support, and ref management
- Update existing call sites for the new CommitOptions field and
  create_worktree signature
@rtfeldman rtfeldman self-assigned this Apr 6, 2026
@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Apr 6, 2026
@zed-community-bot zed-community-bot bot added the staff Pull requests authored by a current member of Zed staff label Apr 6, 2026
@rtfeldman rtfeldman force-pushed the persist-worktree-3-wiring branch 3 times, most recently from 1657263 to 4266294 Compare April 6, 2026 14:35
@rtfeldman rtfeldman force-pushed the persist-worktree-2-archived-model branch from 7f05a6c to 33b4871 Compare April 6, 2026 14:51
@rtfeldman rtfeldman force-pushed the persist-worktree-3-wiring branch from 4ce9048 to 0c1eb65 Compare April 6, 2026 14:52
@rtfeldman rtfeldman force-pushed the persist-worktree-3-wiring branch 10 times, most recently from 253b49a to a37b7df Compare April 6, 2026 18:27
Anthony-Eid and others added 8 commits April 6, 2026 14:52
This argument already gets added when calling git_binary.run()
Now creating a worktree from a detached head also has remote support
This goes against what git does internally and could cause testing bugs
in the future
GitStore::stage_all already does everything this method does and updates
pending_ops as well. So I'm removing this method to avoid a potential
foot gun in our codebase
- resolve_commit on Repository entity: wraps revparse_batch to check
  if a commit SHA exists in the repo. Used during restore to verify
  original_commit_hash is present before attempting recovery.
- repair_worktrees on GitRepository trait + RealGitRepository (runs
  git worktree repair) and FakeGitRepository (no-op). Used during
  restore when a worktree directory exists on disk but may not be in
  git's worktree metadata.
- repair_worktrees wrapper on Repository entity.
@rtfeldman rtfeldman force-pushed the persist-worktree-2-archived-model branch from 33b4871 to c2284f4 Compare April 6, 2026 20:34
@rtfeldman rtfeldman force-pushed the persist-worktree-3-wiring branch from a37b7df to 8456bbd Compare April 6, 2026 20:34
rtfeldman added 16 commits April 7, 2026 22:47
Use futures::channel::oneshot for the archive cancellation signal and
futures::channel::mpsc::unbounded for the DB operations queue, replacing
smol::channel usage. Remove the smol dependency from the sidebar crate.
When the last thread on a linked worktree is archived, a '+ New Thread'
entry with the worktree chip appears in the sidebar, keeping the worktree
alive and preventing disk cleanup. This test documents the current buggy
behavior so we can fix it.
When archiving the last thread on a linked worktree, the sidebar would
create a '+ New Thread' entry with the worktree chip, keeping the
worktree alive and preventing disk cleanup.

Root cause: archive_thread archived the metadata (triggering
rebuild_contents via cx.notify()) before switching the active workspace
away from the linked worktree. rebuild_contents then saw the threadless
linked worktree as active and emitted the spurious entry.

Fix: switch away from the active thread BEFORE archiving metadata, and
activate the target workspace in the MultiWorkspace so rebuild_contents
never sees the threadless linked worktree as active.

Extracted switch_away_from_thread as a separate method and added
activate_workspace helper to ensure the MultiWorkspace is updated.
Adapted archive_thread to main's new structure (workspace_to_remove +
archive_and_activate pattern) while preserving our fix: activating the
target workspace in MultiWorkspace to prevent rebuild_contents from
emitting a spurious '+ New Thread' entry on the linked worktree.

Removed orphaned ArchiveStatus enum and archive_worktree function body
that were superseded by main's workspace removal approach.
Rewire sidebar thread archival to start the in-flight worktree archive
task again, so archiving the last thread for a linked worktree removes
the worktree from disk instead of only updating sidebar state.

This also makes root planning and worktree-state persistence more
robust when a live linked-worktree repository handle is not already
available, and strengthens the sidebar test to assert the worktree
directory is actually deleted.
Restore smol::channel for the DB operations queue and archive
cancellation signal, reverting the futures::channel change from
e1c73e7. smol channels provide bounded backpressure and are a
better fit here.
@rtfeldman rtfeldman marked this pull request as ready for review April 8, 2026 17:58
rtfeldman added a commit that referenced this pull request Apr 8, 2026
…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
#53215 lands)

Release Notes:

- N/A
@rtfeldman rtfeldman merged commit 2169eba into main Apr 8, 2026
30 checks passed
@rtfeldman rtfeldman deleted the persist-worktree-3-wiring branch April 8, 2026 21:04
lukemarsden added a commit to helixml/zed that referenced this pull request Apr 9, 2026
Merge upstream zed-industries/zed main branch (7544515) into Helix fork.

Notable upstream changes:
- Agent V2 feature flag removal (zed-industries#52792)
- Sidebar/MultiWorkspace rework (zed-industries#53096, zed-industries#53032, zed-industries#53267)
- Agent panel git worktree/branch pickers (zed-industries#52979)
- Language model crate refactor (zed-industries#53270)
- Notification panel removal (zed-industries#50204)
- Subagent permission requests (zed-industries#53428)
- Worktree archival on thread archive (zed-industries#53215)
- Agent layout onboarding (zed-industries#52940)
- AuthRequired status for context servers (zed-industries#53396)

Conflict resolutions:
- crates/zed/Cargo.toml: kept external_websocket_sync feature + upstream track-project-leak
- crates/agent_ui/Cargo.toml: kept external_websocket_sync feature + upstream audio feature
- crates/agent_ui/src/agent_panel.rs: preserved all cfg-gated WebSocket blocks,
  migrated should_render_onboarding to should_render_new_user_onboarding,
  added upstream agent_layout_onboarding
- crates/agent/src/tools/context_server_registry.rs: added AuthRequired to match arm,
  preserved pending_tool_loads tracking
- crates/workspace/src/workspace.rs: added new upstream re-exports
- crates/title_bar/src/title_bar.rs: preserved Helix connection status indicator,
  dropped always-hidden onboarding banner (upstream removed it too)
- crates/recent_projects/src/dev_container_suggest.rs: kept suggest_dev_container check +
  upstream cli_auto_open
- .github/workflows, assets/keymaps, assets/settings, editor, multi_workspace: took upstream

Spec-Ref: helix-specs@e637a591b:001723_merge-latest-zed
github-actions bot pushed a commit that referenced this pull request Apr 9, 2026
…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
#53215 lands)

Release Notes:

- N/A
zed-zippy bot added a commit that referenced this pull request Apr 9, 2026
…root (#53443) (cherry-pick to preview) (#53565)

Cherry-pick of #53443 to preview

----
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
#53215 lands)

Release Notes:

- N/A

Co-authored-by: Richard Feldman <richard@zed.dev>
rtfeldman added a commit that referenced this pull request Apr 10, 2026
…ive (#53215)

Connect the git API and archived worktree data model to the sidebar's
archive/unarchive flow:

- Add `thread_worktree_archive` module: orchestrates the full archive
cycle (WIP commits, DB records, git refs, worktree deletion) and restore
cycle (detached worktree creation, reset to recover staged/unstaged
state, branch restoration)
- Integrate into sidebar: `archive_thread` now persists worktree state
before cleanup; `activate_archived_thread` restores worktrees via git
with targeted path replacement for multi-root threads
- Add pending worktree restore UI state with spinner and cancel button
- Show toast on restore failure instead of silent log
- Switch to surviving workspace when archiving active thread whose
workspace will be deleted
- Deserialize persisted `project_group_keys` on window restore
- Guard `cleanup_empty_workspaces` against dropped entities
- Await rollback DB operations instead of fire-and-forget

Part 3 of 3 in the persist-worktree stack. Stacked on #53214. This wires
up parts 1 and 2.

Release Notes:

- Added worktree state preservation when archiving agent threads,
automatically restoring staged and unstaged changes when the thread is
unarchived

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>
maxbrunsfeld added a commit that referenced this pull request Apr 10, 2026
Cherry-picked PRs (in order applied):

1. #53386
2. #53400
3. #53396
4. #53428
5. #53356
6. #53215
7. #53429
8. #53458
9. #53436
10. #53451
11. #53454
12. #53419
13. #53287
14. #53521
15. #53463
16. #52848
17. #53544
18. #53556
19. #53566
20. #53579
21. #53575
22. #53550
23. #53585
24. #53510
25. #53599
26. #53099
27. #53662
28. #53660
29. #53657
30. #53654


Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Eric Holk <eric@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Cameron Mcloughlin <cameron.studdstreet@gmail.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
Co-authored-by: Katie Geer <katie@zed.dev>
Co-authored-by: ojpro <contact@ojpro.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants