Claude
Skills
Sign in
Back

refresh-repo

Included with Lifetime
$97 forever

Check PR merge readiness, sync local repo, cleanup stale worktrees; optional cross-repo sweep and stale-branch prune modes

General

What this skill does


<!-- cspell:words refspec oneline headRefOid mergedAt -->

# Git Refresh

Check open PR merge-readiness status, sync the local repository, and cleanup stale worktrees.
**Note**: Does not automatically merge PRs - only reports readiness status for each PR.

> **State warning**: Branch state, remote tracking, and PR status change between
> invocations. Re-run all git/gh commands from Step 1.

## Steps

### 1. Identify Open PRs

**CRITICAL**: Always check for open PRs, regardless of current branch.

```bash
# Check for PR from current branch
gh pr view --json state,number,title 2>/dev/null

# ALWAYS also check for any open PRs by the user
gh pr list --author @me --state open --json number,title,headRefName
```

### 2. Report Merge-Readiness Status

For each open PR, **DO NOT MERGE** - only check and report.

Run the **canonical PR-readiness gate** from /gh-cli-patterns.
Replace `<OWNER>`, `<REPO>`, `<PR_NUMBER>` per the placeholder legend in that skill.

**Merge-ready criteria** — all of the following must hold:

| Field | Required | Status |
|---|---|---|
| `state` | `OPEN` | Not ready |
| `mergeable` | `MERGEABLE` | Not ready |
| `mergeStateStatus` | `CLEAN` or `HAS_HOOKS` | Not ready (`BEHIND`, `BLOCKED`, `DIRTY`, `UNSTABLE`, `UNKNOWN`, `DRAFT`) |
| `isDraft` | `false` | Not ready |
| `reviewDecision` | `APPROVED` or `null` | Not ready |
| `statusCheckRollup.state` | `SUCCESS` | Not ready |
| All `reviewThreads.isResolved` | `true` | Not ready — unresolved threads |
| `reviewThreads.pageInfo.hasNextPage` | `false` | Not ready — >100 threads, paginate |

### 3. Sync Workflow

1. Record the current branch and worktree path.
2. Fetch origin with stale remote branch pruning, but without tag updates:
   `git fetch origin --no-tags --prune --force`
3. Determine the default branch from `origin/HEAD`, falling back to `main` or `master`.
4. **Restore the default-branch worktree to the default branch.** If a
   worktree is checked out to the default branch, keep it on the default
   branch. After a feature PR merges, that worktree is sometimes left on the
   now-`[gone]` feature branch. Detect and fix:
   - Resolve the default worktree path from `git worktree list --porcelain`,
     matching on the `branch refs/heads/<default>` entry — do not rely on
     basename matching of paths, since a feature branch named
     `feature/<default>` would also produce a path basename of `<default>`.
   - If that path exists and `git -C <path> rev-parse --abbrev-ref HEAD` does not equal
     `<default>` (this is safer than `symbolic-ref --short HEAD`, which errors on
     detached HEAD during a rebase or commit-checkout):
     - If the worktree has uncommitted changes
       (`git -C <path> status --porcelain` is non-empty), stash them first with
       `git -C <path> stash push -u -m "refresh-repo: auto-stash before <default> restore"`
       and surface the stash reference in the summary so the user can recover.
     - `git -C <path> checkout <default>`.
   - Never use `--force`, never discard uncommitted work, never reset.
5. Sync the default branch from its dedicated worktree with a fast-forward only merge,
   using `git -C <path>` so the merge always targets the default worktree regardless
   of the current shell directory:
   `git -C <path> merge --ff-only origin/<default>`.
   If the default worktree is dirty or divergent, report it and skip instead of resetting.
6. Delete local branches already merged into the default branch with `git branch -d`.
   Never delete main/master/develop/current branches, worktree-checked-out branches, or branches
   with open PRs.
7. Conclude the operation without switching branches. Because Steps 4 and 5 used
   `git -C <path>` to operate on the default worktree directly, the current shell's
   working directory and branch were never changed — each worktree owns its checkout.

Do not use `git fetch --tags`, `git fetch --prune-tags`, or `git pull --tags` during the
normal refresh. Tags are audited separately in Step 4 so local-only non-release tags and
tag rewrites are not deleted by a broad fetch refspec.

### 4. Tag Audit And Cleanup

Treat `origin` as authoritative for release tags only.

Use native Git commands to compare local tags to remote tags:

```bash
git for-each-ref '--format=%(refname:short)' refs/tags
git show-ref --tags
git ls-remote --tags --refs origin
```

For local-only tags:

1. If the tag name matches the release tag pattern `v[0-9]*`, delete it with
   `git tag -d <tag>`.
2. If the tag name does not match the release tag pattern, report it and do not delete it.

For tags that exist both locally and on origin but point at different objects, report the
mismatch and do not force-update it automatically. Never delete or rewrite remote tags.

### 5. Worktree Cleanup

Only remove a worktree if it is confirmed stale.

**Stale definition**: No open PR, no uncommitted changes, and either:

- The branch has a merged PR (most recently merged by `mergedAt`) whose `headRefOid` matches the local branch `HEAD`
  (`gh pr list --state merged --head <branch> --json number,headRefOid,mergedAt`)
- Its remote tracking branch was deleted (`[gone]` in `git branch -vv`) and it has no commits
  ahead of the default branch (`git log origin/<default>..HEAD --oneline` is empty)

Branches with open PRs, local-only branches without PRs, and worktrees with uncommitted
changes are **NEVER** stale.

For each worktree from `git worktree list`:

1. Skip the default branch worktree, the current branch, and bare repo entries
2. If the branch has an open PR, skip — it is **never** stale
3. Check if stale using the definition above
4. If not stale, skip
5. Run `git worktree remove <path>` — **NEVER use `--force`**
6. If Git blocks removal (dirty worktree), report it and skip
7. If removed, also delete the branch: `git branch -d <branch>`.
   If this fails only because a squash-merged branch is not reachable from local default,
   use `git branch -D <branch>` only when the merged PR `headRefOid` matched the local
   branch `HEAD` before removing the worktree.

Finish with `git worktree prune`.

### 6. Summary

Report: PRs assessed as merge-ready (if any), tags deleted or reported, branches cleaned up,
worktrees removed, default-branch worktree restorations (with any stash references created),
current branch, and sync status.

## Cross-Repo Operating Modes

Optional modes that change `/refresh-repo` from single-repo to workspace-wide.
Both modes reuse the stale-worktree definition from Step 5 and the deletion
rules from Step 5.7 — they only add new pre-filters, never weaken existing
safety.

### `--sweep [<repo-glob>]`

Multi-repo cleanup of abandoned local branches. For every main worktree
in your workspace (caller can pass a custom glob if their layout differs),
for every local branch where `git log origin/main..HEAD` is non-empty:

1. **Content-equivalence check**: compute merge base, diff each touched file
   against current `origin/main`. If every touched file is content-equivalent
   to (or older than) `origin/main`, delete the branch and its worktree.
   Already-on-main contributions do not deserve a PR.
2. **Workaround filter**: if the diff (a) modifies 1 of N files sharing a
   common shape with no written rationale for the asymmetry, or (b) references
   a "sync mechanism" / "auto-update" by name that `grep -r <name> .` returns
   zero matches for, surface for human review. Do not open a draft.
3. Only branches passing both checks become draft PRs.
4. Per-repo summary: branches deleted as content-equivalent, branches surfaced
   for review, branches PR-ified, branches unchanged.

**Origin**: the 2026-05-22 sweep opened 8 dead PRs against `ansible-splunk`
(6 already-on-main duplicates, 2 workaround anti-patterns). Both filters
above would have caught all 8 before any CI ran.

### `--prune-stale <days>` (default 60)

Delete local branches with no open PR and no push activity within `<days>`.
Expands Step 5's stale definition with a time threshold

Related in General