feat(repo): add prune command to delete merged local branches

Description

Adds glab repo prune, which deletes local Git branches whose source branch has at least one merged merge request on GitLab and no still-open MRs from the same branch.

This replaces the workflow served by gitlab-org/gitlab's scripts/prune_merged_branches and — unlike git branch --merged — correctly identifies branches merged via squash and rebase strategies (which rewrite commit SHAs and look like distinct commits to Git).

Behavior

  • Lists local branches via git for-each-ref refs/heads/.
  • For each branch, fetches all pages of merge requests with that branch as the source.
  • A branch is deletable only if it has ≥1 merged MR and zero open/locked MRs from the same source — the conservative safe choice when a branch name has been reused.
  • The default branch, the currently checked-out branch, and protected branches (fetched from the GitLab API) are always excluded.
  • --exclude adds extra patterns. Wildcards follow GitLab's protected-branch semantics: * matches any character (including /), ? matches exactly one.
  • --merged skips only the per-branch MR lookup and falls back to git branch --merged (fast-forward only). The default branch and protected branches are still fetched from GitLab in this mode.
  • --dry-run previews; --yes skips the confirmation prompt. Non-interactive without --yes is a hard error.
  • Command is marked mcpannotations.Destructive: "true".

Flags

Flag Description
--dry-run Preview branches that would be deleted.
-y, --yes Skip the confirmation prompt.
-e, --exclude Branch name or glob pattern to exclude. Comma-separated or repeated.
--merged Use git branch --merged instead of the GitLab API.

Closes #8142 (closed)

How has this been tested?

  • Unit tests covering: happy path, dry-run, protected-branch exclusion (including GitLab wildcard patterns like release* that must span /), user --exclude patterns, current-branch skipping, the merged+open-MR safety case, MR pagination (open MR on a later page must veto deletion), the --merged git-native path, non-interactive guard, protected-branches API failure, and the wildcard matcher itself.
  • Local smoke test via glab repo prune --help.
  • go vet, gofmt, package and adjacent tests all pass.

Reviewer notes

The original implementation has been substantively reworked in response to review:

  • Per-branch MR lookup is now paginated (was missing open MRs on later pages).
  • The wildcard matcher now uses GitLab semantics so release* excludes release/1.0 (the old filepath.Match did not span /).
  • --include-current was removed — git branch -D refuses the checked-out branch, so the flag could never do anything useful.
  • Production code now calls client.Projects.GetProject and client.MergeRequests.ListProjectMergeRequests directly so tests use the gitlabtesting mock client throughout, with t.Parallel() on every test.
Edited by Kai Armstrong

Merge request reports

Loading