Skip to content

project_panel: Multi-worktree drag reordering and collaboration sync - #60656

Open
eth0net wants to merge 1 commit into
zed-industries:mainfrom
eth0net:feature-worktree-drag-reorder
Open

project_panel: Multi-worktree drag reordering and collaboration sync#60656
eth0net wants to merge 1 commit into
zed-industries:mainfrom
eth0net:feature-worktree-drag-reorder

Conversation

@eth0net

@eth0net eth0net commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #55755 (now merged), which restored single-worktree drag-and-drop reordering. This PR adds the larger feature work that was split out so the regression fix could land quickly. Rebased onto main, so the diff is feature-only.

What this adds

  • Reorder marked worktree roots together as a contiguous group when dragged, preserving their relative order. Direction (before vs after the destination) follows the active source's original position, matching single-source semantics.
  • Root drops are only accepted onto another worktree's root; the drag-over highlight matches for roots-only drags, while mixed drags (root + file) still highlight directory targets so the file portion gets feedback.
  • Blank-area drops of a root group that includes the last worktree send the group to the end instead of no-opping on the self-drop guard.
  • Honor the copy modifier in root drag feedback and the blank-area path, refreshing highlights on modifier change.
  • WorktreeStore::move_worktrees / move_worktrees_to_end batch the reorder and validate every source up front, erroring on missing IDs.

Discussion points (raised in #55755 review)

  • Collaboration order sync. Reorders now call send_project_updates so collaborators see the new worktree order. Worktree order was intentionally not synced during collaboration so far — guests keep their own panel order — so this is a behavior change worth confirming before merge.
  • Group-move semantics. The group-reorder behavior is the part flagged as needing iteration. Happy to adjust.

Behavior change vs the merged fix

The minimal fix (#55755) added test_drag_including_worktree_root_only_reorders, which asserted that a drag containing a worktree root only reorders worktrees. This PR deliberately changes that space:

  • Root-onto-root reorders (single and group).
  • Root-onto-nested-entry drops are now rejected rather than reordering the whole worktree (see test_drag_worktree_root_onto_nested_entry_is_rejected).
  • Mixed selections with a non-root active selection reorder the root and move the file (test_drag_mixed_root_and_file_with_non_root_active); copy-drags filter roots out but still copy the non-root entries (test_copy_drag_mixed_worktree_root_and_file_still_copies_file).

I updated test_drag_including_worktree_root_only_reorders to reflect the new semantics. Flagging it explicitly since it touches a test that just merged.

Release Notes:

  • Improved drag and drop to reorder multiple worktrees at once

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Jul 9, 2026
@eth0net
eth0net force-pushed the feature-worktree-drag-reorder branch from 5096ab8 to 55c559b Compare July 9, 2026 13:55
@eth0net
eth0net marked this pull request as ready for review July 9, 2026 13:56
Copilot AI review requested due to automatic review settings July 9, 2026 13:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds multi-worktree drag-and-drop reordering in the project panel, including root-only drop targeting/highlighting rules, “send to end” behavior for blank-area drops, and new WorktreeStore/Project APIs to perform grouped reorders (with collaboration update propagation).

Changes:

  • Implement grouped worktree-root reordering (preserving relative order) and a dedicated move-to-end path for root groups.
  • Refine drag highlighting and drop acceptance: root-only drags only target roots; copy-modifier behavior is reflected in both drop handling and highlight invalidation.
  • Introduce/extend tests covering grouped reorders, rejected nested-entry drops for roots, modifier toggling, and new worktree-store move APIs.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/project/tests/integration/project_tests.rs Adds integration tests for move_worktrees_to_end and invalid-id error behavior.
crates/project/src/worktree_store.rs Adds move_worktrees / move_worktrees_to_end, updates reorder paths to emit events/notify and send project updates.
crates/project/src/project.rs Exposes new worktree move APIs on Project as wrappers over WorktreeStore.
crates/project_panel/src/project_panel.rs Implements root-group reorder logic, blank-area move-to-end routing, and updated drag highlight/copy-modifier handling.
crates/project_panel/src/project_panel_tests.rs Adds extensive coverage for new drag semantics (group reorder, rejection rules, copy-mode highlighting, modifier-change invalidation).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 4890 to +4912
let update_marks = !self.marked_entries.is_empty();
let active_selection = selections.active_selection;
let active_entry_id = self.resolve_entry(active_selection.entry_id);

// Reorder marked worktree roots together so their relative order is
// preserved; non-roots fall through to the normal per-entry move flow.
let (root_entry_ids, entries) = {
let project = self.project.read(cx);
let mut roots = Vec::new();
let mut non_roots = BTreeSet::new();
for entry in entries {
if project.entry_is_worktree_root(entry.entry_id, cx) {
roots.push(entry.entry_id);
} else {
non_roots.insert(entry);
}
}
(roots, non_roots)
};

if !root_entry_ids.is_empty() {
self.reorder_worktree_roots(&root_entry_ids, target_entry_id, active_entry_id, cx);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in cb2cb1f. You're right that the flow moved non-root entries after reordering (the mixed-drag test only looked like it left files in place because move_worktree_entry treats a cross-worktree move as a no-op when the relative path happens to be identical — a coincidence of the test data, not intended behavior).

drag_onto now checks whether the active selection is a worktree root. If it is, the drag is treated as a pure reorder gesture: it reorders the roots and returns without moving any non-root entries in the same drag. Files are only moved when the drag is initiated from a non-root entry (test_drag_mixed_root_and_file_with_non_root_active / test_drag_marked_root_with_nested_file_keeps_both both use a non-root active selection and still move the file). Updated test_drag_including_worktree_root_only_reorders to assert this deterministically (with explicit file-location checks rather than relying on the relative-path coincidence).

Comment thread crates/project/src/worktree_store.rs Outdated
Comment on lines 1062 to 1067
// Self-drop of any selection member is a no-op: the user dropping a
// multi-selection onto one of its own roots has no well-defined
// intent.
if sources.contains(&destination) {
return Ok(());
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cb2cb1f. Moved the id validation (destination + every source) ahead of the self-drop early-return, so move_worktrees(&[invalid], invalid, ..) now errors with Missing worktree for id <id> instead of being masked by the self-drop no-op. This matches the upfront-validation contract used elsewhere in the PR. Covered by test_move_worktree_with_invalid_source_errors.

@eth0net
eth0net force-pushed the feature-worktree-drag-reorder branch from 55c559b to cb2cb1f Compare July 9, 2026 14:47
Builds on the drag-and-drop reorder regression fix with the larger
feature work:

- Reorder marked worktree roots together as a contiguous group when
  dragged, preserving their relative order. Direction (before vs after
  the destination) follows the active source's original position.
- A drag whose active selection is a worktree root is treated as a pure
  reorder gesture: it reorders the roots and does not move any non-root
  entries marked in the same drag. Files are only moved when the drag is
  initiated from a non-root entry.
- Root drops are only accepted onto another worktree's root; the
  drag-over highlight matches for roots-only drags, while mixed drags
  still highlight directory targets for the file portion.
- Blank-area drops of a root group that includes the last worktree send
  the group to the end instead of no-opping on the self-drop guard.
- Honor the copy modifier in root drag feedback and the blank-area path,
  refreshing highlights on modifier change.
- Reorders call `send_project_updates` so collaborators see the new
  worktree order.
- `WorktreeStore::move_worktrees` / `move_worktrees_to_end` batch the
  reorder and validate every source up front (before the self-drop
  no-op), erroring on missing IDs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@eth0net
eth0net force-pushed the feature-worktree-drag-reorder branch from cb2cb1f to e9ccef2 Compare July 9, 2026 14:48
@dinocosta dinocosta added the area:project panel Feedback for files tree view label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:project panel Feedback for files tree view cla-signed The user has signed the Contributor License Agreement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants