Skip to content

feat(ui): drag-and-drop Git actions (DAN-90) - #158

Merged
danielss-dev merged 1 commit into
mainfrom
developements/drag-and-drop-git-actions-7781
Oct 19, 2026
Merged

danielss-dev merged 1 commit into
mainfrom
developements/drag-and-drop-git-actions-7781

Conversation

@danielss-dev

@danielss-dev danielss-dev commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Linear: https://linear.app/daniels-dev/issue/DAN-90/strand-drag-and-drop-git-actions

Common Git operations can be done by dragging. Every drop stops on a confirmation that names the source, the target, and the operation. Escape cancels with no checkout and no Git write. Keyboard and menu commands are unchanged and call the same functions.

Slices

  1. Drop a branch on a branch: confirm Merge or Rebase, including remote-tracking branches in either direction.
  2. Drop a local branch on a remote folder: confirm Push, and offer to create an upstream when the branch has none.
  3. Drop graph commits on a local branch: confirm Cherry-pick (oldest first). One merge commit opens the existing mainline dialog. A multi-selection that includes a merge is refused.
  4. Drag commits on the linear tip of the current branch: the interactive rebase editor opens with that reorder or squash filled in. Git runs only when Start rebase is pressed.

Out of scope: dragging changes or hunks into commits.

Menu and palette equivalents

  • Merge — sidebar Merge into {branch} (local rows, and the same item on remote-tracking rows) opens MergeDialog.
  • Rebase — sidebar Rebase {branch} onto this runs Repo::rebase after its confirm.
  • Push — sidebar Push to remote… opens the branch network dialog, which sends Repo::push_branch.
  • Cherry-pick — graph Cherry-pick, Cherry-pick N selected commits, and the commit detail action. A merge uses Cherry-pick merge… / MainlineDialog.
  • Reorder / squash — command palette and branch menu Interactive rebase…, or Rebase from here… on a commit, then ⌥↑/⌥↓ and s in RebaseEditor.

Proof

  • Each drop names the source, the target, and the operation. Escape is covered by finishDrag(session, false) in ui/src/lib/gitDrag.test.ts, and the demo session closed every confirm with Escape before any further action. A no-drop release also confirms nothing.
  • Fixture tests in ui/fixtures/gitDrag.fixture.test.ts run the drag argv against real git and compare it to the menu command: feature onto main Merge (git merge --no-edit -- feature), Rebase (git rebase -- feature), the swapped direction after checkout, push of feature onto origin with --set-upstream (branch.feature.remote=origin, branch.feature.merge=refs/heads/feature), cherry-pick, and a prefilled rebase todo for reorder and squash.
  • Invalid drops resolve to null and use the no-drop cursor: a branch onto itself, onto a tag, a remote-tracking branch onto another remote-tracking branch, and any drop while meta.operation is merge or rebase. A commit off the linear tip, a drop onto itself, and a reorder that would not move anything also resolve to null.
  • Drop-target resolution is unit-tested in ui/src/lib/gitDrag.test.ts and ui/src/lib/rebase.test.ts.
  • pnpm --filter ./ui test — 106 files, 653 tests, green. pnpm --filter ./ui exec tsc --noEmit — green.

The confirm shots below were taken after popIn and fadeIn finished. The first set was captured on the frame those animations were still at opacity 0, which let the graph show through. The dialog is the shared Dialog shell: opaque var(--bg-panel) and the same palette-backdrop as the other confirms.

Merge or rebase confirmation after dropping feature/auth-retry on main
Push confirmation offering to set origin/spike/edge-runtime as upstream
Cherry-pick confirmation naming the commit and main
Interactive rebase editor prefilled to squash one commit into another
No-drop cursor while dragging main onto itself

To show artifacts inline, enable in settings.

Review fixes

  • Commit rows use one commitIndex for both data-git-index and the selection.
  • Confirms name the branch that will be checked out first. If the drop action fails after that checkout, the original branch is checked out again, and the error says whether the restore worked.
  • Drops are blocked during any in-progress operation (cherry-pick, revert, am, bisect, as well as merge and rebase).
  • MergeDialog / MainlineDialog take a wrap step instead of prepare.
  • Tests are in gitDrag.test.ts.
  • Round 2:
    • After a failed drop step, Strand re-reads meta.operation. It switches back only when nothing is in progress. Otherwise it stays on the target branch and says the operation is paused there. It also stays when the state can't be read, and a detached start says where it was left.
    • commitIndexByHash (graph rows minus stash rows) is extracted for Commits.tsx. The stash-index test now uses interleaved stash rows.

Merge order 157 → 153 → 155 → 154 → 156 → 158: this PR merges last. It must adapt to #155's rebase(onto, updateRefs) and RebaseOntoDialog, #154's App rebaseDialog state, and #156's Commits.tsx attribute.

Open in Web Open in Cursor 

Squashed for the rebase onto #155, #154 and #156. Drag-rebase now opens the
same RebaseOntoDialog as the Rebase menu (with the --update-refs choice).
@danielss-dev
danielss-dev marked this pull request as ready for review October 19, 2026 15:30
@danielss-dev
danielss-dev force-pushed the developements/drag-and-drop-git-actions-7781 branch from 01bd0a7 to aecd3f9 Compare October 19, 2026 15:30
@danielss-dev
danielss-dev merged commit 4f08a8a into main Oct 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants