Finishing a Development Branch

SkillDev tools

Use when implementation is complete and tests pass but the branch itself is not yet resolved — verify tests, detect worktree vs. normal checkout, then present merge/PR/keep/discard options and execute the chosen cleanup

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Finishing a Development Branch skill

What this skill tells your AI

The instructions your AI receives, as published by drvoss/everything-copilot-cli in skills/workflow/finishing-a-development-branch/SKILL.md and read by ahel’s review.

commit-workflow gets changes into commits, release ships a version, and github-pr-workflow manages PRs that already exist. None of them answer the question that comes right after implementation finishes: what happens to this branch now? This skill closes that gap.

Core principle: Verify tests → detect environment → present options → execute the choice → clean up.

When to Use

  • Implementation on a feature/fix branch is complete and its own tests pass
  • You need to decide between merging locally, opening a PR, leaving the branch for later, or discarding it — and the workspace (worktree vs. normal checkout) affects how cleanup should work
  • A subagent-driven-development-style task loop (see fleet-parallel §5-A) just finished its final whole-branch review and needs a clean handoff

When NOT to Use

Instead of finishing-a-development-branchUse
You need conventional commit messages while still workingcommit-workflow
The branch is already merged and you are cutting a versionrelease
You need PR-lifecycle operations (labels, review requests, merge) once a PR existsgithub-pr-workflow
You just need to create or remove a worktree, independent of a merge decisionusing-git-worktrees

Workflow

1. Verify tests pass

npm test  # or the project's actual test command

If tests fail, stop here. Report the failures and do not proceed to any option below — a failing branch has nothing to finish yet.

2. Detect the workspace environment

$gitDir = git rev-parse --git-dir
$gitCommon = git rev-parse --git-common-dir
StateMenuCleanup behavior
$gitDir == $gitCommonStandard 4 options (below)No worktree involved
$gitDir != $gitCommon, named branch (a worktree)Standard 4 optionsWorktree removed only for Merge/Discard, per Step 4
$gitDir != $gitCommon, detached HEADReduced 3 options (no local merge)Externally managed — do not remove

3. Determine the base branch

git merge-base HEAD main 2>$null
if (-not $?) { git merge-base HEAD master 2>$null }

If neither resolves, ask which branch this work should integrate into.

4. Present options and execute the choice

Normal repo or named-branch worktree:

Implementation complete. What would you like to do?
1. Merge back to <base-branch> locally
2. Push and create a Pull Request
3. Keep the branch as-is
4. Discard this work

Detached HEAD:

Implementation complete (detached HEAD — externally managed workspace).
1. Push as a new branch and create a Pull Request
2. Keep as-is
3. Discard this work

Keep the menu terse — do not pad it with explanation.

OptionActionWorktree cleanup
Merge locallySee Merging from a worktree below, then delete the branchYes — remove after merge succeeds
Push + PRNamed branch: git push -u origin <branch>. Detached HEAD: git checkout -b <new-branch-name> first, then git push -u origin <new-branch-name>. Either way, hand off to github-pr-workflowNo — the worktree must stay alive for review iteration
Keep as-isReport the branch name (or commit SHA, if detached) and worktree path; do nothing elseNo
DiscardRequire the user to type the literal word discard to confirm, listing the commits and worktree path that will be deleted. Named branch: force-delete the branch, then remove the worktree. Detached HEAD: this workspace is externally managed (see Step 2) — do not remove it or the branch; report which commits are being abandoned and let the host harness reclaim the workspaceNamed branch only — remove before/instead of deleting the branch. Never for detached HEAD

Never remove a worktree for "Push + PR" or "Keep as-is" — the whole point of those two options is that the workspace must remain usable afterward. Never remove a detached-HEAD workspace yourself under any option — Step 2 already established it is externally managed.

Merging from a worktree

If the base branch is already checked out somewhere else (the common case when this branch lives in its own worktree), running git checkout <base> from inside the feature worktree fails with fatal: '<base>' is already checked out at .... Merge from the worktree/checkout that actually owns the base branch instead of the feature worktree:

# Resolve the main checkout (where the base branch actually lives)
$mainRoot = git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel

# Merge in the main checkout, not the feature worktree
Push-Location $mainRoot
git checkout <base-branch>
git pull
git merge <feature-branch>
<test command>   # re-verify tests on the merged result before cleanup
Pop-Location

Only proceed to worktree cleanup (Step 5) and branch deletion after the merge and its test run both succeed. If $mainRoot equals the current directory (normal repo, no worktree), the Push-Location/Pop-Location pair is a no-op and the merge runs in place as expected.

5. Clean up the workspace (Merge and Discard only)

$worktreePath = git rev-parse --show-toplevel
  • If $gitDir == $gitCommon: no worktree exists — nothing to remove.

  • If the worktree lives under a path this workflow (or using-git-worktrees) created, remove it and prune stale registrations from the main checkout:

    $mainRoot = git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel
    Push-Location $mainRoot
    git worktree remove $worktreePath
    git worktree prune
    Pop-Location
    
  • Otherwise, a host harness or externally managed environment owns the workspace — do not remove it yourself. Use the platform's own workspace-exit mechanism if one exists, or leave it in place.

Examples

Finishing a fleet task-loop branch

After fleet-parallel §5-A's final whole-branch review passes:

> Implementation complete. What would you like to do?
> 1. Merge back to main locally
> 2. Push and create a Pull Request
> 3. Keep the branch as-is
> 4. Discard this work
"2" — push and open a PR so the team can review before merge.

Discarding an abandoned spike

> This will permanently delete:
> - Branch spike/cache-experiment
> - 4 commits
> - Worktree at .worktrees/spike-cache
> Type 'discard' to confirm.
"discard"

Common Rationalizations

RationalizationReality
"Tests passed in CI earlier, skip re-checking now"Re-verify at the point of decision — commits may have landed since the last CI run.
"I'll just delete the worktree, the branch can go too"Options 2 and 3 exist specifically because the worktree must survive; always match cleanup to the chosen option.
"Discard is obviously fine, no need to list what's lost"Force-delete is irreversible — always show the exact branch, commits, and path before requiring explicit confirmation.

Red Flags

  • A branch is merged or discarded without first re-running its tests
  • A worktree is removed after "Push and create a Pull Request" or "Keep as-is"
  • A discard proceeds without the user typing the literal confirmation word
  • The workspace state (worktree vs. normal checkout, attached vs. detached HEAD) was never checked before presenting options

Verification

  • Tests were re-verified immediately before presenting options
  • The workspace state (worktree/detached HEAD) was detected before choosing a menu
  • The executed action matches the option chosen — no extra cleanup on Push/Keep, no missing cleanup on Merge/Discard
  • Discard required an explicit typed confirmation naming what will be deleted

See Also

Signals

GitHub stars
46
Forks
11
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
finishing-a-development-branch-drvoss
Source
github.com/drvoss/everything-copilot-cli