Skip to content

Add cancel button and spinner for pending worktree restores - #53241

Closed
rtfeldman wants to merge 26 commits into
mainfrom
persist-worktree-4-cancel-restore
Closed

rtfeldman wants to merge 26 commits into
mainfrom
persist-worktree-4-cancel-restore

Conversation

@rtfeldman

Copy link
Copy Markdown
Contributor

Track in-flight worktree restores in the Sidebar via a HashSet<SessionId>. While a restore is pending, the thread item shows a spinning "Restoring worktree…" indicator with a cancel button that appears on hover. Clicking cancel clears the pending state.

This is purely UI — the restore itself still runs to completion in the background; cancel just hides the spinner.

Part 4 of 4 in the persist-worktree stack. Stacked on #53215.

Release Notes:

  • N/A

…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 from 0c1eb65 to c2e9b36 Compare April 6, 2026 14:57
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from 37af681 to ee5892b Compare April 6, 2026 14:57
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from c2e9b36 to b237dd3 Compare April 6, 2026 15:07
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch 2 times, most recently from 2057637 to 378b44e Compare April 6, 2026 15:19
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch 2 times, most recently from 5bb4eca to 74a134d Compare April 6, 2026 15:27
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch 2 times, most recently from 38563bf to 715cd48 Compare April 6, 2026 15:34
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch 2 times, most recently from 5e119b8 to 35bdf32 Compare April 6, 2026 15:51
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch 2 times, most recently from 3298aed to f8f897a Compare April 6, 2026 15:55
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from 35bdf32 to ff8e591 Compare April 6, 2026 15:55
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from f8f897a to d75ae03 Compare April 6, 2026 15:55
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from ff8e591 to a00fddb Compare April 6, 2026 15:56
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch 2 times, most recently from 959f1bb to d1a406d Compare April 6, 2026 16:12
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from a00fddb to 253b49a Compare April 6, 2026 16:12
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from d1a406d to a99d6b5 Compare April 6, 2026 18:27
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from 253b49a to a37b7df Compare April 6, 2026 18:27
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from d1063b7 to 1bfc40e Compare April 6, 2026 20:55
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from 1bfc40e to 8f89b75 Compare April 6, 2026 21:02
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch 2 times, most recently from c816b57 to 0fad306 Compare April 6, 2026 21:05
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from 8f89b75 to 6280b00 Compare April 6, 2026 21:05
Anthony-Eid and others added 4 commits April 6, 2026 17:07
Add the persistence layer for tracking archived git worktrees:

- ArchivedGitWorktree struct with staged_commit_hash and
  unstaged_commit_hash fields to precisely identify WIP commits
- DB migrations for archived_git_worktrees and thread_archived_worktrees
  (join table) tables
- CRUD operations: create, link to thread, query by thread, delete
- Column impl for deserializing ArchivedGitWorktree from DB rows
- Tests for create/retrieve with distinct SHAs, delete cascading
  through join table, multi-thread linking, and multiple worktrees
  per thread
- New field on ArchivedGitWorktree struct storing the HEAD SHA from
  before WIP commits were created during archival. Used as a
  pre-restore sanity check, fallback reset target, and post-restore
  verification.
- DB migration adding original_commit_hash column, backfilled from
  the legacy commit_hash column for existing rows.
- Updated create_archived_worktree signature, SQL INSERT, and bindings
  on both ThreadMetadataDb and ThreadMetadataStore.
- Updated SELECT query and Column impl to read the new field.
- Updated all 4 archived worktree tests.
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
- Show toast on restore failure instead of silent log
- Deserialize persisted project_group_keys on window restore
- Guard cleanup_empty_workspaces against dropped entities
- Await rollback DB operations instead of fire-and-forget
- If worktree already exists on disk when unarchiving, reuse it as-is
  instead of auto-generating a new path
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from bb27cf7 to b9379b1 Compare April 6, 2026 21:30
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from 809aa0f to 75c0e9d Compare April 6, 2026 21:30
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
persist_worktree_state:
- Read HEAD SHA before creating WIP commits as original_commit_hash
- Pass it to create_archived_worktree

restore_worktree_via_git:
- Pre-restore: verify original_commit_hash exists via resolve_commit;
  abort with user-facing error if the git history is gone
- Worktree-already-exists: check for .git file to detect if path is a
  real git worktree; if not, call repair_worktrees to adopt it
- Resilient WIP resets: track success of mixed and soft resets
  independently; if either fails, fall back to mixed reset directly to
  original_commit_hash
- Post-reset HEAD verification: confirm HEAD landed at
  original_commit_hash after all resets
- Branch restoration: after switching, verify branch points at
  original_commit_hash; if it doesn't, reset and create a fresh branch
Track in-flight worktree restores in the Sidebar via a
HashSet<SessionId>. While a restore is pending, the thread item shows
a spinning 'Restoring worktree…' indicator with a cancel button that
appears on hover. Clicking cancel clears the pending state.

This is purely UI — the restore itself still runs to completion in the
background; cancel just hides the spinner.
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from b9379b1 to e9783e6 Compare April 6, 2026 21:39
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch 2 times, most recently from 9f93a1a to e3f89e1 Compare April 6, 2026 21:48
- worktree_restore_tasks HashMap on Sidebar tracks in-flight restore
  operations keyed by SessionId
- Restore task is now stored (not detached) so it can be tracked and
  the pending state cleaned up when it completes
- cancel_worktree_restore drops the task from the map and notifies
- ThreadItem wired up with pending_worktree_restore bool and
  on_cancel_restore callback from the Sidebar's render_thread
- Action slot (archive/stop buttons) hidden while a restore is pending
- Reconciled with existing PR4 restore spinner in worktree labels
@rtfeldman
rtfeldman force-pushed the persist-worktree-4-cancel-restore branch from e3f89e1 to fa56504 Compare April 6, 2026 21:55
@rtfeldman
rtfeldman force-pushed the persist-worktree-3-wiring branch from e9783e6 to 1860c47 Compare April 7, 2026 20:09
Base automatically changed from persist-worktree-3-wiring to main April 8, 2026 21:04
@rtfeldman

rtfeldman commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of #53463

@rtfeldman rtfeldman closed this Apr 9, 2026
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.

2 participants