feat(merge): delete source branch on merge — repo policy + labctl pr merge flags #130

Open
opened 2026-07-11 11:01:31 +02:00 by dominik.polakovics · 0 comments

What to build

A repo-level delete_branch_on_merge policy (default off) plus per-call overrides --delete-branch / --keep-branch on labctl pr merge, so that merging a PR also deletes its remote head branch on the forge. Today nothing ever deletes the forge-side branch: gitx.DeleteBranch is a local git branch -D in the bare mirror only, and the Forgejo client explicitly omits delete_branch_after_merge — every merged PR leaves a dead remote branch behind.

End-to-end behavior:

  • New repo setting delete_branch_on_merge (default off), editable in RepoSettings and PATCH-able via RepoUpdate.
  • labctl pr merge <n> gains mutually-exclusive --delete-branch / --keep-branch; the flag is threaded as an optional tri-state field on POST /agent/v1/prs/{n}/merge and overrides the repo policy for that call. No flag → policy applies.
  • Managed heads only: deletion happens only when the PR's head matches the repo's AFK branch pattern or manual branch prefix, and never the default branch. An explicit --delete-branch on an unmanaged (human) head is a refused no-op with a notice in the response; the merge itself still succeeds. Mirrors the ManagedBranch scoping the sweep already uses (internal/gitx/worktree.go:187).
  • Mechanism per binding: Forgejo sets delete_branch_after_merge: true in the merge body when deletion resolves on; GitHub issues an explicit ref delete after merge; builtin changes nothing (its head lives in the local bare repo where the run's worktree may still be checked out — the existing guarded teardown already deletes clean+merged branches).
  • Local bare-mirror cleanup stays with the existing teardown/sweep, unchanged.

Acceptance criteria

  • Repo setting delete_branch_on_merge exists, defaults off, surfaced in RepoSettings UI and PATCH-able
  • labctl pr merge accepts --delete-branch / --keep-branch (mutually exclusive), threaded through the agent API; no flag → repo policy decides
  • Deletion only for heads matching the AFK pattern or manual prefix, never the repo default branch; explicit --delete-branch on an unmanaged head → merge succeeds, deletion refused with a notice
  • Forgejo uses delete_branch_after_merge in the merge request; GitHub deletes the ref after merge; builtin merge behavior unchanged
  • AFK reaper done-signal still fires for a merged PR whose head was deleted (merged PRs outlive their head) — covered by a test
  • Tests cover policy on/off × flag unset/delete/keep × managed/unmanaged head across tracker bindings

Blocked by

None - can start immediately

## What to build A repo-level **`delete_branch_on_merge`** policy (default **off**) plus per-call overrides **`--delete-branch` / `--keep-branch`** on `labctl pr merge`, so that merging a PR also deletes its remote head branch on the forge. Today nothing ever deletes the forge-side branch: `gitx.DeleteBranch` is a local `git branch -D` in the bare mirror only, and the Forgejo client explicitly omits `delete_branch_after_merge` — every merged PR leaves a dead remote branch behind. End-to-end behavior: - New repo setting `delete_branch_on_merge` (default off), editable in RepoSettings and PATCH-able via `RepoUpdate`. - `labctl pr merge <n>` gains mutually-exclusive `--delete-branch` / `--keep-branch`; the flag is threaded as an optional tri-state field on `POST /agent/v1/prs/{n}/merge` and overrides the repo policy for that call. No flag → policy applies. - **Managed heads only**: deletion happens only when the PR's head matches the repo's AFK branch pattern or manual branch prefix, and never the default branch. An explicit `--delete-branch` on an unmanaged (human) head is a refused no-op with a notice in the response; the merge itself still succeeds. Mirrors the `ManagedBranch` scoping the sweep already uses (`internal/gitx/worktree.go:187`). - Mechanism per binding: **Forgejo** sets `delete_branch_after_merge: true` in the merge body when deletion resolves on; **GitHub** issues an explicit ref delete after merge; **builtin** changes nothing (its head lives in the local bare repo where the run's worktree may still be checked out — the existing guarded teardown already deletes clean+merged branches). - Local bare-mirror cleanup stays with the existing teardown/sweep, unchanged. ## Acceptance criteria - [ ] Repo setting `delete_branch_on_merge` exists, defaults off, surfaced in RepoSettings UI and PATCH-able - [ ] `labctl pr merge` accepts `--delete-branch` / `--keep-branch` (mutually exclusive), threaded through the agent API; no flag → repo policy decides - [ ] Deletion only for heads matching the AFK pattern or manual prefix, never the repo default branch; explicit `--delete-branch` on an unmanaged head → merge succeeds, deletion refused with a notice - [ ] Forgejo uses `delete_branch_after_merge` in the merge request; GitHub deletes the ref after merge; builtin merge behavior unchanged - [ ] AFK reaper done-signal still fires for a merged PR whose head was deleted (merged PRs outlive their head) — covered by a test - [ ] Tests cover policy on/off × flag unset/delete/keep × managed/unmanaged head across tracker bindings ## Blocked by None - can start immediately
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#130
No description provided.