finish refuses the rebase strategy outright when the topic branch has its own separate linked worktree (#245), rather than support it: git flow feature finish x --rebase from a repo where feature/x lives in a linked worktree fails with a clear, actionable error naming --merge/--squash as alternatives, or removing the worktree first. Merge and squash both work correctly from inside such a worktree (#175); only rebase is unsupported.
Why it's not supported yet
A first attempt (tried and reverted in #245) ran the rebase directly in the topic's own worktree, since the branch is already checked out there and refs are shared across worktrees of one repository — avoiding the "already used by worktree" checkout failure that a naive redirect hits. This worked for the conflict-free path, but broke on conflict: reproduced by building and running it directly, not just reasoning about it.
mergestate's JSON state lands in the redirected worktree (the parent's own worktree, or main), since that is where handleMergeStep calls SaveMergeState. Git's own rebase state (rebase-merge/REBASE_HEAD) lands in the topic worktree, since that is where the rebase itself ran. One operation, split across two git-dirs.
- Mid-rebase, the topic worktree is on a detached HEAD (rebase always operates that way internally, reattaching only on success).
WorktreeForBranch matches on the porcelain branch field, so it returns nothing for a detached worktree — the lookup that --continue/--abort would need, to locate the redirected state, goes blind exactly when it matters.
- Concretely:
--continue after resolving the conflict fails with "no merge in progress" (the state lookup never finds it, not even the exit-6 refusal a narrower reading of the bug would predict). --abort exits 0 having silently done nothing — the rebase, the detached HEAD, and the stranded merge.json are all left exactly as they were.
What real support needs
At least six places currently assume the operation's mergestate, git's own rebase markers, and the conflicted working tree all live in the same worktree, and would each need to account for a split location:
- The
--continue/--abort state-lookup fallback in executeFinish (needs to find state whether it's local or redirected, and separately needs to handle the topic worktree's detached-HEAD blindness — not specific to this feature, worth fixing as its own primitive improvement).
mergestate's staleness check (isStateValid), which currently checks IsGitRebaseInProgress() against the state-holding repo's own git-dir.
handleContinue's HasConflicts() call.
RebaseContinue()/RebaseAbort()'s dispatch — needs to run against whichever repo actually holds the rebase.
preflightWorktreeCleanup's in-progress detection, which would otherwise see the operation's own rebase as a foreign blocker.
handleAbort's topic-worktree lookup, which also goes blind mid-rebase for the same detached-HEAD reason as (1).
The likely shape of a real fix: persist the rebase's actual worktree path in MergeState at the point the rebase starts, and thread a second repo handle through handleContinue/handleAbort for the strategy-specific git calls, while the redirected repo continues to own everything else (the merge-into-parent step, cleanup). The detached-HEAD blindness in (1)/(6) needs its own fix — likely keying the lookup off mergestate's persisted branch name rather than the worktree's current (possibly detached) HEAD — and is worth landing as a small standalone fix regardless of this issue, since any future feature that keys off "which worktree holds branch X" during an in-progress operation would hit the same trap.
Relationship to #175 and #230
#175 established worktree cleanup for finish/delete generally; #230 fixed finish being unable to run at all from inside a topic's own worktree, for merge/squash. This issue is the one combination neither closed: rebase strategy specifically, from inside a worktree, surviving a conflict.
finishrefuses the rebase strategy outright when the topic branch has its own separate linked worktree (#245), rather than support it:git flow feature finish x --rebasefrom a repo wherefeature/xlives in a linked worktree fails with a clear, actionable error naming--merge/--squashas alternatives, or removing the worktree first. Merge and squash both work correctly from inside such a worktree (#175); only rebase is unsupported.Why it's not supported yet
A first attempt (tried and reverted in #245) ran the rebase directly in the topic's own worktree, since the branch is already checked out there and refs are shared across worktrees of one repository — avoiding the "already used by worktree" checkout failure that a naive redirect hits. This worked for the conflict-free path, but broke on conflict: reproduced by building and running it directly, not just reasoning about it.
mergestate's JSON state lands in the redirected worktree (the parent's own worktree, or main), since that is wherehandleMergeStepcallsSaveMergeState. Git's own rebase state (rebase-merge/REBASE_HEAD) lands in the topic worktree, since that is where the rebase itself ran. One operation, split across two git-dirs.WorktreeForBranchmatches on the porcelainbranchfield, so it returns nothing for a detached worktree — the lookup that--continue/--abortwould need, to locate the redirected state, goes blind exactly when it matters.--continueafter resolving the conflict fails with "no merge in progress" (the state lookup never finds it, not even the exit-6 refusal a narrower reading of the bug would predict).--abortexits 0 having silently done nothing — the rebase, the detached HEAD, and the strandedmerge.jsonare all left exactly as they were.What real support needs
At least six places currently assume the operation's mergestate, git's own rebase markers, and the conflicted working tree all live in the same worktree, and would each need to account for a split location:
--continue/--abortstate-lookup fallback inexecuteFinish(needs to find state whether it's local or redirected, and separately needs to handle the topic worktree's detached-HEAD blindness — not specific to this feature, worth fixing as its own primitive improvement).mergestate's staleness check (isStateValid), which currently checksIsGitRebaseInProgress()against the state-holding repo's own git-dir.handleContinue'sHasConflicts()call.RebaseContinue()/RebaseAbort()'s dispatch — needs to run against whichever repo actually holds the rebase.preflightWorktreeCleanup's in-progress detection, which would otherwise see the operation's own rebase as a foreign blocker.handleAbort's topic-worktree lookup, which also goes blind mid-rebase for the same detached-HEAD reason as (1).The likely shape of a real fix: persist the rebase's actual worktree path in
MergeStateat the point the rebase starts, and thread a second repo handle throughhandleContinue/handleAbortfor the strategy-specific git calls, while the redirected repo continues to own everything else (the merge-into-parent step, cleanup). The detached-HEAD blindness in (1)/(6) needs its own fix — likely keying the lookup offmergestate's persisted branch name rather than the worktree's current (possibly detached) HEAD — and is worth landing as a small standalone fix regardless of this issue, since any future feature that keys off "which worktree holds branch X" during an in-progress operation would hit the same trap.Relationship to #175 and #230
#175 established worktree cleanup for finish/delete generally; #230 fixed finish being unable to run at all from inside a topic's own worktree, for merge/squash. This issue is the one combination neither closed: rebase strategy specifically, from inside a worktree, surviving a conflict.