wip
Run on /wip or for a whole-repository cleanup spanning multiple worktrees and branches. Trigger when the request combines operations such as removing stale worktrees, rebasing all branches, retesting,
Files1
View on GitHubSKILL.mdskills/2546-git/9898-wip/SKILL.md
GitHub/wip — work-in-progress cleanup pipeline
Drive a repo full of leftover worktrees and half-finished branches toward a clean, landed state. Four phases run in order, each gated:
- Worktrees: remove every worktree except the main and protected ones, freeing their branches.
- Rebase + GC: rebase every branch onto upstream main (
j g/git-all) and delete merged branches. - Test: retest the rebased commits.
- Push: push the green branches. The user opens and merges PRs.
Opening PRs, CI, review, and merging are the user's call. If the repo or user documents a WIP state model, follow it for those stages.
Golden Rules
- Never
--force. Remove worktrees withgit worktree remove --no-force(write the flag explicitly) and push with a pinned--force-with-lease. Git's refusal is the safety net for uncommitted work and unexpected remote changes. - The worktree and branch set is a moving target. Other agents and the user create, switch, and merge while you work. Recompute with
git worktree list --porcelain/git for-each-refimmediately before each action, and re-git fetchbefore testing and pushing. - Gate irreversible and outward-facing steps. Removing a worktree with WIP, force-pushing, and
delete-merged(which deletes remote branches) need the user's OK. Until the flow is proven smooth in a repo, ask at each phase boundary. - Keep the main checkout clean. Rebases and tests check out commits, so run them in throwaway worktrees (
git worktree add) and tear those down afterward.
Protected Worktrees
The main worktree is the first entry of git worktree list --porcelain (the one whose gitdir is git rev-parse --git-common-dir). Never remove it.
Permanent worktrees are listed per repo in .llm/wip.json:
{"protectedWorktrees": ["/Users/you/projects/some-permanent-worktree"]}
If the file is missing, only the main worktree is protected, but ask before removing any long-lived worktree you're unsure about. git worktree lock also works, since a locked worktree can't be removed without --force.
Phase 1 — Worktree Cleanup
- Compute the removal set live:
git worktree list --porcelainminus main minus protected. Use plaingit worktree list, never a user shell alias likegit worktrees. - Remove them one at a time from the main repo via the
git:clean-worktreesskill, writing the flag explicitly:git -C <main> worktree remove --no-force <dir>. - Stop on the first failure and never retry with
--force. A dirty worktree fails withfatal: '<dir>' contains modified or untracked files, use --force to delete it(exit 128). Showgit -C <dir> status --shortand ask whether to commit the WIP (single line, viagit:commit), stash it, or leave the worktree. - A brand-new clean, empty worktree (0 commits ahead, no changes) is probably someone about to start work. Surface it rather than deleting it.
- Finish with
git -C <main> worktree prune --verbose.
Phase 2 — Rebase Every Branch + GC
Use the git-all script (rebase-all → git worktree prune → delete-merged; the user may alias it as j g) rather than bare rebase-all, so merged branches get cleaned up. It needs UPSTREAM_REMOTE (default upstream; many repos use origin, so check git remote -v and the project's .envrc). origin/main in these examples and in references/pipeline.md stands for $UPSTREAM_REMOTE/main. Substitute it when the upstream isn't origin, or every rebase and "all clean" check runs against a stale base and silently reports success.
- Report the scope first. Every local branch not containing upstream main can be dozens (stale
dev,main4, experiments,pr*-fix). Count them and let the user confirm. - Conflicts.
git-allhalts on the first conflict mid-rebase. Resolve genuine conflicts with thegit:conflictsskill. Verifygit rererereplays rather than trusting them, and beware false-positive conflict-marker greps in files that legitimately contain=======(ASCII banners, markdown headings). delete-mergedis outward-facing. It also runsgit push --delete origin <branch>for merged remote branches (excluding main/HEAD/origin/pr/*). Gate it.
Read references/pipeline.md before this phase. It covers the co-pointed-branch fallback (when several branch names point at one commit, rebase-all skips them all and you must rebase the leftovers directly) and the worktree-per-branch parallel rebase.
Afterward, git for-each-ref refs/heads/ --no-contains origin/main should be empty.
Phase 3 — Test the Rebased Commits
Rebasing invalidates prior test results, so every rebased branch needs a retest. Re-git fetch first.
Run build:test-all (per branch: git test run <FLAGS> origin/main..BRANCH) in a dedicated worktree, since git-test checks out each commit. See references/pipeline.md for the worktree and env setup and the flaky-retest rule.
Phase 4 — Push Only
Record each branch's remote sha (git rev-parse refs/remotes/origin/<branch>) before re-git fetching, then push every branch ahead of upstream main:
- If
refs/remotes/origin/<branch>exists:git push --force-with-lease=<branch>:<recorded-sha> origin <branch>:<branch>. Pin the sha: a bare--force-with-leasebaselines on the remote-tracking ref, which the re-fetch just advanced, so it would clobber a teammate's new commit instead of refusing. - Otherwise:
git push origin <branch>:<branch>.
Don't open or merge PRs unless asked. Expect per-push permission prompts.
Ask vs. Automate
Automate: detecting main, reading the protected list, computing sets live, worktree remove --no-force, worktree prune, clean rebases, tests, and pinned --force-with-lease pushes of branches the user has scoped.
Ask: what to do with a dirty worktree, whether to remove a brand-new one, the scope of a big rebase or push batch, how to resolve a genuine conflict, and any outward step (delete-merged, force-push, merge).