Git Post-Merge Repository & Branch Cleanup Skill
This skill defines the official protocol and step-by-step procedures for cleaning up local development branches, pulling the latest main from GitHub after a Pull Request is merged, and executing deep Git repository garbage collection and maintenance.
1. Strict Permission & Trigger Boundary
main. 2. The human user reviews and merges the Pull Request on GitHub. 3. The human user prompts the AI agent to perform local cleanup. 4. The AI agent executes the safe post-merge cleanup runbook below.2. Core Cleanup Principles
main and pull from origin/main first so that the newly merged PR code and history are fully present locally before deleting the feature branch.main.git fetch --prune origin) to keep the remote tracking tree clean.git reflog expire --expire=now --all) and optimize the local object store (git gc --prune=now --aggressive) to keep the .git directory small, fast, and healthy.main remains as the active local branch.3. Step-by-Step Cleanup Runbook
When the human user requests post-merge cleanup, execute these exact steps in sequence:
Step 1: Verify Working Directory State
Ensure there are no unstaged modifications or stash conflicts:
git statusIf dirty, stop and check if there are uncommitted files that need to be preserved.
Step 2: Switch to main and Pull Latest Upstream
Switch to the primary release branch and pull the merged changes from GitHub:
git switch main
git pull origin mainStep 3: Prune Remote Tracking Branches
Synchronize and remove remote branch references that were deleted on GitHub after PR merge:
git fetch --prune originStep 4: Identify & Delete Local Feature Branches
List all local branches to inspect what needs to be removed:
git branchDelete all local feature branches (e.g. <github-username>/main/<feature>):
# Safe delete merged branches:
git branch -d <branch-name>
# If GitHub used Squash & Merge or Rebase & Merge (which changes commit hashes):
git branch -D <branch-name>Or delete all local branches except main in one command:
git branch | grep -v "^\* main$" | grep -v "^ main$" | xargs -r git branch -DStep 4b: Prune & Remove Ephemeral Worktrees
If the merged feature branch was checked out in a dedicated worktree:
# 1. Identify worktree path
git worktree list
# 2. Safely remove the worktree directory (assert clean status first)
git worktree remove .worktrees/wt-<branch-slug>
# 3. Prune internal git worktree metadata
git worktree pruneStep 5: Expire Reflogs & Run Aggressive Garbage Collection
Clean up dangling commits, unreachable objects, and optimize packfiles:
git reflog expire --expire=now --all
git gc --prune=now --aggressiveStep 6: Verify Final Repository State
Verify that only main remains and the working tree is clean:
git branch
git status4. Automated Remote Branch Cleanup on PR Close (GitHub Actions)
.github/workflows/pr-branch-cleanup.yml deployed. This workflow is the automated counterpart to the manual local cleanup runbook above. Without it, merged feature branches accumulate on GitHub and pollute the remote branch list.Remote feature branch deletion is automated via a GitHub Actions workflow that triggers on every PR close event (both merged and closed-without-merge). The canonical workflow is:
# .github/workflows/pr-branch-cleanup.yml
name: "PR Closed — Remote Branch Cleanup"
on:
pull_request:
types: [closed]
permissions:
contents: read
jobs:
cleanup-closed-branch:
name: "Delete Closed Remote Branch"
if: >
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.head.ref != 'main' &&
github.event.pull_request.head.ref != 'master'
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: "Delete Remote Feature Branch"
uses: actions/github-script@<full-commit-sha> # vX.Y.Z (current major, pinned by SHA)
with:
script: |
const headRef = context.payload.pull_request.head.ref;
try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${headRef}`
});
core.info(`[SUCCESS] Deleted remote branch: ${headRef}`);
} catch (error) {
if (error.status === 422 || error.status === 404) {
core.info(`[INFO] Remote branch '${headRef}' was already deleted; skipping.`);
} else {
core.setFailed(`[ERROR] Failed to delete remote branch '${headRef}': ${error.message}`);
}
}Safety guarantees baked into the workflow:
main or master.Relationship to local cleanup (Sections 2–3 above):