Closes the worktree lifecycle: finish and delete free a branch's worktree as part of their operation — removing the ones git-flow created, detaching the ones it did not — with guards against data loss and safe navigation when the user is inside a worktree being removed. Part of #171. Refs #45.
Goal
When a branch goes away, clean up the worktree git-flow created for it, leave a worktree the user created themselves in place, and move the user somewhere valid if they were working inside a directory that is being removed.
Expected Behavior
finish and delete gain:
| Flag |
Short |
Description |
--keep-worktree |
|
Keep the worktree; detach it from the branch instead of removing it |
--force-worktree |
-W |
Remove a git-flow-created worktree even with uncommitted or untracked changes |
Because a branch checked out in a linked worktree cannot be deleted while it is checked out, worktree handling and branch deletion are coupled: the worktree has to be freed before the branch can go. What freeing it means depends on provenance (#172):
- git-flow created the worktree — it is removed, and its marker cleared.
- the user created it — it is kept, and its HEAD is detached off the branch. The directory and every file in it stay exactly as they were, including uncommitted work, because detaching at the same commit does not touch the working tree. The command reports what it did.
--keep-worktree applies the detach path to a git-flow-created worktree as well: the directory is kept, detached, and the branch is still deleted.
Both commands pre-flight the worktree before any destructive step, so a refused cleanup can never follow a completed merge. If a git-flow-created worktree has uncommitted or untracked changes and --force-worktree is not given, the command aborts early and leaves both the worktree and the branch intact, with guidance. Detaching never needs a force, since it changes no files — but if the worktree has an operation in progress (merge, rebase, bisect) it cannot be detached, and the command aborts the same way.
finish: once the pre-flight passes, perform the merge/rebase, then free the worktree and delete the branch. delete: the same, without the merge.
If the user is currently inside a worktree that is being removed, the parent branch's worktree — or the main worktree when the parent has none — is written to the navigation channel from #172 before the removal. The detach path never moves the user, because the directory stays where it is.
Branches with no worktree behave exactly as today, and so does a branch checked out in the main worktree; the flags are no-ops in both cases.
Test Scenarios
feature finish x where feature/x has a clean git-flow-created worktree — merges per config, removes the worktree, clears its marker, deletes the branch.
feature finish x where the worktree was created by hand with git worktree add — merges, detaches the worktree's HEAD, deletes the branch; the directory and its files are untouched and git worktree list still shows the path, now detached.
feature finish x --keep-worktree on a git-flow-created worktree — merges, detaches, deletes the branch, and keeps the directory.
feature finish x where a git-flow-created worktree has uncommitted changes and no -W — aborts before merging; worktree and branch left intact, with guidance. With -W, the merge proceeds and the worktree is removed.
feature finish x where a hand-made worktree has uncommitted changes — no force needed; merges and detaches, and the uncommitted changes are still there afterwards.
feature finish x where the worktree has a merge or rebase in progress — aborts before merging, with guidance; nothing is removed or detached.
finish run from inside a git-flow-created worktree — records the parent branch's worktree as the destination, falling back to the main worktree.
finish run from inside a hand-made worktree — nothing is recorded; the directory stays and the shell stays with it.
delete x with a clean git-flow-created worktree — removes worktree and branch.
delete x where the worktree was created by hand — deletes the branch and detaches the worktree.
delete x --keep-worktree — deletes the branch and keeps the directory on a detached HEAD.
delete x with untracked files in a git-flow-created worktree and no -W — refuses; with -W it removes.
delete x run from inside its git-flow-created worktree — records the main worktree as the destination, then removes.
delete x -D -W on an unmerged branch with a dirty git-flow-created worktree — force-deletes both branch and worktree.
finish/delete on a branch with no worktree — behavior unchanged; the new flags are no-ops.
finish/delete on a branch checked out in the main worktree — behavior unchanged; no worktree handling applies.
Out of Scope
Worktree hooks (pre-worktree-remove is added in #177) and list integration.
Technical Notes
Uses the removal, detach, provenance, and navigation operations from #172. Provenance comes only from the marker written at creation time by start (#173), checkout (#174), and worktree add (#172) — never from matching the worktree path against the template, since --worktree-path and later template changes both break that correspondence. The parent worktree is resolved from the branch's parent config, falling back to the main worktree. Dirty detection uses git status --porcelain against the worktree. The main worktree is never removed or detached. The pre-flight has to cover the --continue path too: after a conflicted merge is resolved, finish still reaches branch deletion, and it must not arrive there with an unfreeable worktree.
Closes the worktree lifecycle:
finishanddeletefree a branch's worktree as part of their operation — removing the ones git-flow created, detaching the ones it did not — with guards against data loss and safe navigation when the user is inside a worktree being removed. Part of #171. Refs #45.Goal
When a branch goes away, clean up the worktree git-flow created for it, leave a worktree the user created themselves in place, and move the user somewhere valid if they were working inside a directory that is being removed.
Expected Behavior
finishanddeletegain:--keep-worktree--force-worktree-WBecause a branch checked out in a linked worktree cannot be deleted while it is checked out, worktree handling and branch deletion are coupled: the worktree has to be freed before the branch can go. What freeing it means depends on provenance (#172):
--keep-worktreeapplies the detach path to a git-flow-created worktree as well: the directory is kept, detached, and the branch is still deleted.Both commands pre-flight the worktree before any destructive step, so a refused cleanup can never follow a completed merge. If a git-flow-created worktree has uncommitted or untracked changes and
--force-worktreeis not given, the command aborts early and leaves both the worktree and the branch intact, with guidance. Detaching never needs a force, since it changes no files — but if the worktree has an operation in progress (merge, rebase, bisect) it cannot be detached, and the command aborts the same way.finish: once the pre-flight passes, perform the merge/rebase, then free the worktree and delete the branch.delete: the same, without the merge.If the user is currently inside a worktree that is being removed, the parent branch's worktree — or the main worktree when the parent has none — is written to the navigation channel from #172 before the removal. The detach path never moves the user, because the directory stays where it is.
Branches with no worktree behave exactly as today, and so does a branch checked out in the main worktree; the flags are no-ops in both cases.
Test Scenarios
feature finish xwherefeature/xhas a clean git-flow-created worktree — merges per config, removes the worktree, clears its marker, deletes the branch.feature finish xwhere the worktree was created by hand withgit worktree add— merges, detaches the worktree's HEAD, deletes the branch; the directory and its files are untouched andgit worktree liststill shows the path, now detached.feature finish x --keep-worktreeon a git-flow-created worktree — merges, detaches, deletes the branch, and keeps the directory.feature finish xwhere a git-flow-created worktree has uncommitted changes and no-W— aborts before merging; worktree and branch left intact, with guidance. With-W, the merge proceeds and the worktree is removed.feature finish xwhere a hand-made worktree has uncommitted changes — no force needed; merges and detaches, and the uncommitted changes are still there afterwards.feature finish xwhere the worktree has a merge or rebase in progress — aborts before merging, with guidance; nothing is removed or detached.finishrun from inside a git-flow-created worktree — records the parent branch's worktree as the destination, falling back to the main worktree.finishrun from inside a hand-made worktree — nothing is recorded; the directory stays and the shell stays with it.delete xwith a clean git-flow-created worktree — removes worktree and branch.delete xwhere the worktree was created by hand — deletes the branch and detaches the worktree.delete x --keep-worktree— deletes the branch and keeps the directory on a detached HEAD.delete xwith untracked files in a git-flow-created worktree and no-W— refuses; with-Wit removes.delete xrun from inside its git-flow-created worktree — records the main worktree as the destination, then removes.delete x -D -Won an unmerged branch with a dirty git-flow-created worktree — force-deletes both branch and worktree.finish/deleteon a branch with no worktree — behavior unchanged; the new flags are no-ops.finish/deleteon a branch checked out in the main worktree — behavior unchanged; no worktree handling applies.Out of Scope
Worktree hooks (
pre-worktree-removeis added in #177) andlistintegration.Technical Notes
Uses the removal, detach, provenance, and navigation operations from #172. Provenance comes only from the marker written at creation time by
start(#173),checkout(#174), andworktree add(#172) — never from matching the worktree path against the template, since--worktree-pathand later template changes both break that correspondence. The parent worktree is resolved from the branch's parent config, falling back to the main worktree. Dirty detection usesgit status --porcelainagainst the worktree. The main worktree is never removed or detached. The pre-flight has to cover the--continuepath too: after a conflicted merge is resolved,finishstill reaches branch deletion, and it must not arrive there with an unfreeable worktree.