Skip to content

feat: virtual-branch workspace prototype (per-file/hunk assignment, drag & drop, persistence) - #361

Open
angeousta wants to merge 7 commits into
Auto-Explore:devfrom
VibeCodders:feat/virtual-branches-workspace
Open

feat: virtual-branch workspace prototype (per-file/hunk assignment, drag & drop, persistence)#361
angeousta wants to merge 7 commits into
Auto-Explore:devfrom
VibeCodders:feat/virtual-branches-workspace

Conversation

@angeousta

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a virtual-branch workspace: a way to work on several logically-separate changes in the working tree at the same time, before they become real Git commits/branches, by assigning individual files (or even individual diff hunks) in the working tree to lightweight, in-progress "virtual branches." It is a first, deliberately scoped prototype of a workflow popularized by tools like GitButler — applied here specifically to GitComet's own data model and UI, not a port of another tool's code.

Why

Git's working tree is single-stream by construction: everything you've touched sits together in one undifferentiated pile of changes, and splitting that pile into several clean, independent commits/branches after the fact is manual, error-prone work — you have to remember which edit belonged to which idea, stage hunks by hand, and hope you don't accidentally mix two unrelated changes into the same commit.

In practice, this shows up constantly during normal development: you set out to fix one bug, and along the way you also touch a couple of unrelated files (a small cleanup here, an unrelated fix there) because that's where you happened to be working. At commit time you're now stuck manually untangling those changes with git add -p or a similar dance, or you give up and ship them together in one oversized, thematically-mixed commit — which then makes for a worse PR and a worse git log.

This feature moves that separation earlier and makes it continuous rather than a one-time cleanup step: as you edit, you assign each changed file (or a specific hunk within a file, see "Move hunk to virtual branch" below) to the virtual branch it conceptually belongs to, while you're still working, rather than reconstructing that grouping afterward from memory. When you're done, each virtual branch already represents one clean, coherent set of changes, ready to be committed and turned into a real branch/PR with no untangling step.

What's included

This PR bundles five commits that build up the feature incrementally — each is a complete, working step on top of the previous one, and the branch is intended to be reviewed and merged as a whole (see "Why one PR" below), but the commit history is kept intact and readable rather than squashed:

  1. Virtual-branch workspace prototype with per-branch file assignment — the foundational piece: a sidebar section listing virtual branches, a context-menu action on any changed file in the Status/Changes panel to assign it to a (new or existing) virtual branch, and the underlying state model (gitcomet-state) that tracks which paths belong to which virtual branch, independent of the actual Git index/worktree.
  2. Move hunk to virtual branch from the diff hunk context menu — extends assignment down to sub-file granularity: a single unstaged hunk can be moved out of the working tree and parked in a virtual branch's own patch collection (the hunk's unified diff is captured and reverse-applied out of the worktree), so a file that mixes two unrelated changes no longer has to be assigned to just one branch as a whole.
  3. Cleanup of stale virtual branches with confirmation — virtual branches that end up with no assigned paths and no parked patches left (e.g. because everything they held was moved elsewhere or reverted) are detected and offered up for removal, with a confirmation prompt, so the sidebar doesn't accumulate empty leftover branches over time.
  4. Drag & drop of worktree files onto virtual branches in the sidebar — the same file-to-branch assignment from step 1, but via drag and drop: rows in the Changes panel become draggable, virtual branch rows in the sidebar become drop targets (with an accent highlight while a drag is hovering), for a faster assignment flow than opening the context menu every time.
  5. Persist virtual branches in session.json — without this, the whole feature would only live in memory for the current app session. This adds serialization of the workspace's virtual-branch state (next-id allocator, per-branch path assignments, applied/unapplied state, parked unified patches) into the existing per-repo session file, saved on every mutation and restored when the repo is reopened or the session is restored — so virtual-branch assignments survive quitting and reopening GitComet, just like every other piece of session state already does.

Scope and status

This is explicitly a prototype-stage feature: it covers the core loop (assign files/hunks to virtual branches, see them grouped in the sidebar, clean up empty ones, persist across sessions) but does not yet include turning a virtual branch into an actual Git commit/branch, or reconciling a virtual branch's parked patches against upstream changes — those are natural, separately-scoped follow-ups once this foundation is in. We think landing the foundation on its own, reviewable as one coherent unit, is preferable to holding it back until the entire end-to-end workflow is done.

Why one PR for five commits

The five commits are sequentially dependent — each one only makes sense with the state model and UI surface the previous ones introduced (there is no clean intermediate state where, say, drag-and-drop assignment is useful without file-assignment already existing). Splitting them into five separate PRs would mean reviewing four of them in a state that doesn't build toward anything usable on its own. The commits are kept separate (rather than squashed into one) so each can still be reviewed and, if needed, bisected independently.

Testing

  • cargo check --workspace passes cleanly on the final state of the branch.
  • cargo fmt --all -- --check passes cleanly.
  • The gitcomet-state virtual-branch test suite passes in full: 22/22 tests (covering assignment, hunk-move/reverse-apply, stale-branch detection, and session persistence/restore round-trips).

Notes for reviewers

This PR was prepared from a fork's branch that had diverged from dev for a while. Porting it forward required resolving real conflicts against upstream changes to several shared UI files (theme token renames, sidebar/status-row helpers that had since changed shape) — those adaptations are folded into the relevant commits so each one still compiles and passes its tests on its own, on top of current dev.

angeousta and others added 7 commits August 10, 2026 09:54
The benchmark's hash_branch_sidebar_rows match was not exhaustive after
adding BranchSidebarRow::VirtualBranchesHeader and ::VirtualBranchItem,
which broke the benches CI job (cargo bench --features benchmarks
--bench performance --no-run) on all platforms.
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.

1 participant