| name | github-complete-pr |
| description | Use when a GitHub pull request has just been merged or closed - switches back to the repository default branch, syncs it, and deletes the now-obsolete feature branch both locally and on the remote if it still exists |
GitHub Complete PR
Overview
Once a pull request is merged or closed, the feature branch is no longer needed. Leaving it around clutters the local repo and the remote, creates stale references, and confuses future git branch / PR listings. This skill defines the cleanup steps to run immediately after PR completion.
When to Use
- A pull request has just been merged (squash, rebase, or merge commit)
- A pull request has been closed without merging and the branch will not be reused
- The user says things like "PR is merged", "done with that PR", "close it out", "clean up the branch"
Do NOT Use When
- The PR is still open or in draft
- The branch is shared with other collaborators who still have in-flight work on it
- The branch is a long-lived branch (e.g.
develop, release/*, main) — never delete these
Rules
1. Confirm the PR is actually merged or closed
Before deleting anything, verify the PR state via gh:
gh pr view <number-or-branch> --json state,mergedAt,headRefName,baseRefName
Only proceed when state is MERGED or CLOSED. If the PR is still OPEN, stop and tell the user.
2. Identify the repository default branch
Do not assume main. Query it:
gh repo view --json defaultBranchRef --jq .defaultBranchRef.name
Use whatever it returns (main, master, trunk, etc.) as the checkout target.
3. Switch back to the default branch and sync
git checkout <default-branch>
git pull --ff-only origin <default-branch>
Use --ff-only to avoid accidental merge commits if the local default branch has drifted.
4. Delete the local feature branch
git branch -d <feature-branch>
Use -d (safe delete) first. Git will refuse when the branch is not fully merged into the default branch — this is common and expected when the PR was squash-merged or rebase-merged (the tip commit of the feature branch is not an ancestor of main).
Force-delete with -D is acceptable only when one of the following is true, confirmed via gh pr view:
state is MERGED — the changes exist on the default branch under a new commit hash; the original commits are redundant.
state is CLOSED and the branch was intentionally abandoned (the user confirms the work will not be reused).
git branch -D <feature-branch>
Data-loss warning. For a CLOSED (unmerged) PR, -D permanently discards any commits on that branch that live nowhere else — there is no remote copy to recover from once the remote branch is also deleted in step 5. Confirm with the user before force-deleting a closed-unmerged branch.
Never use -D without first confirming PR state via gh pr view.
5. Delete the remote branch if it still exists
Many PR workflows auto-delete the remote branch on merge, but not all. Check and clean up:
git fetch --prune origin
if git ls-remote --exit-code --heads origin <feature-branch> >/dev/null 2>&1; then
git push origin --delete <feature-branch>
else
echo "Remote branch already deleted"
fi
--prune removes stale remote-tracking refs. The ls-remote check avoids a noisy error when the remote branch is already gone. The explicit if/else (instead of A && B || C) ensures a real git push --delete failure surfaces as an error rather than being swallowed by the "already deleted" message.
6. Confirm cleanup
Finish by showing the user the state so they can verify:
git branch
git branch -r
The feature branch must be absent from both.
Full Recipe
The recipe below enforces Rule §1 (abort when state is OPEN) and Rule §4 (only force-delete after state validation), and derives FEATURE_BRANCH from the PR itself so a mistyped argument cannot cause the wrong branch to be deleted.
PR=<branch-or-number>
PR_STATE=$(gh pr view "$PR" --json state --jq .state)
FEATURE_BRANCH=$(gh pr view "$PR" --json headRefName --jq .headRefName)
if [ -z "$PR_STATE" ] || [ -z "$FEATURE_BRANCH" ]; then
echo "Could not determine PR state or head branch; aborting."
exit 1
fi
if [ "$PR_STATE" = "OPEN" ]; then
echo "PR is still OPEN; refusing to delete anything."
exit 1
fi
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
if [ -z "$DEFAULT" ]; then
echo "Could not determine default branch (gh auth/config issue, or not a git repo); aborting."
exit 1
fi
if [ "$FEATURE_BRANCH" = "$DEFAULT" ] \
|| [ "$FEATURE_BRANCH" = "main" ] \
|| [ "$FEATURE_BRANCH" = "master" ] \
|| [ "$FEATURE_BRANCH" = ] \
|| [[ == release/* ]];
1
git checkout
git pull --ff-only origin
! git branch -d ;
[ = ];
git branch -D
1
git fetch --prune origin
git ls-remote --exit-code --heads origin >/dev/null 2>&1;
git push origin --delete
git branch
git branch -r
Red Flags — STOP
- PR state is
OPEN → do not delete anything
- Current branch has uncommitted changes → stash or commit before switching
- Feature branch name matches the default branch or a protected pattern (
main, master, develop, release/*) → never delete
- Force-delete (
-D) is needed but PR merge state is unverified → verify first