squash-commits
Skill1 starsChanged 2 months agoMITView on GitHub
Squash unpushed commits into clean, higher-level feat/fix/chore commits that follow the project commit policy. Use when local history has too many small/WIP/fixup commits (common after superhuman or gsd runs), or the user says "squash commits", "squash my commits", "tidy history", "clean up commits", "combine commits before pushing".
3 other projects use a version of this.
Adopted by
Most recently taken up by
- rahulrajaram/rahulskillstheir own version, first seen Oct 13, 2026
- Cybereason-Public/owLSMtheir own version, first seen Oct 8, 2026
- pipecat-ai/pipecattheir own version, first seen Oct 8, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- pipecat-ai/pipecat★ 17ksame name
- Cybereason-Public/owLSM★ 282same name
- rahulrajaram/rahulskills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository61 other files it gives its agents.
- AGENTS.mdAGENTS.md
- skills/code-review/references/agents.mdAGENTS.md
- blind-spotsSkill
- brainstormSkill
- check-agent-logsSkill
- codebase-reviewSkill
- code-reviewSkill
- commitSkill
- debug-logSkill
- deslopSkill
- docSkill
- evaluate-techSkill
- explainSkill
- flutter-upgradeSkill
- guideSkill
- hard-fixSkill
- huhSkill
- ideationSkill
- improve-agent-instructionsSkill
- library-docsSkill
- longshotSkill
- optimize-seoSkill
- pdfSkill
- perf-testSkill
- planSkill
- pre-existingSkill
- qaSkill
- qa-tourSkill
- read-docsSkill
- release-reviewSkill
- research-generalSkill
- research-techSkill
- resolve-conflictsSkill
- review-architectureSkill
- review-cleancodeSkill
- review-cliSkill
- review-commentsSkill
- review-consistencySkill
- review-correctnessSkill
- review-functionalSkill
- review-historySkill
- review-interfacesSkill
- review-library-useSkill
- review-logsSkill
- review-perfSkill
- review-planSkill
- review-productSkill
- review-pythonSkill
- review-reactSkill
- review-rustSkill
- review-securitySkill
- review-swiftSkill
- review-tidySkill
- review-todoSkill
- review-typescriptSkill
- rn-upgradeSkill
- second-opinionSkill
- skill-creatorSkill
- statusSkill
- tempSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
17 rules. 0 are common, 11 are distinctive, and 6 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
11
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Never rewrite pushed commits: Only squash commits not present on any remote.
- Distinctive: line 47
Leave no persistent helper state: Do not create backup tags, branches, or stashes.
- Distinctive: line 48
Leave no temporary files: Use subject-only commit messages, so no message files are needed.
- Distinctive: line 73
If the list is empty → nothing to squash.
- Very specific: line 74
If it includes the root commit (no parent), or there are no remotes at all, auto-detection is unreliable — ask the user for an explicit …
- Distinctive: line 84
If non-empty, stop: this skill only handles linear history.
- Distinctive: line 87
If the range has 0 or 1 commit, there is nothing to combine — say so and stop.
- Very specific: line 108
--conservative: only fold obvious checkpoint/WIP/fixup commits (e.g. - Distinctive: line 117
If a group mixes a feature and an unrelated fix, that's a sign they shouldn't be one group — split them.
- Distinctive: line 125
If reading the diffs leaves a genuine toss-up between materially different groupings or scopes, present the concrete alternatives, …
- Very specific: line 132
Do not create anything. Keep the recorded ORIG_TIP: the original branch still points there until Step 7, and the branch reflog records that …
- Distinctive: line 159
Run one command per group with literal SHAs, copying each printed SHA into the next command.
- Distinctive: line 170
Verify the content is identical: before touching the branch:
- Very specific: line 175
If the tree hashes differ, the last group's tip was not ORIG_TIP — do not move the branch.
- Distinctive: line 178
If both checks pass, point the branch at the rebuilt history:
- Very specific: line 259
Solution: Do not move the branch; it was never touched, so there is nothing to undo.
- Very specific: line 278
Solution: Use the ORIG_TIP printed in the completion report: git reset --soft <ORIG_TIP>.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- Generated from https://github.com/nielsmadan/agentic-coding — edits here are overwritten. -->Squash Commits
Combine the unpushed commits on the current branch into the smallest set of clean,
self-contained commits, each adhering to the commit policy (feat/fix/chore,
no scopes, subject only). Superhuman/gsd-style runs leave many superfluous
commits — initial work, follow-up fixes, tests, docs — that belong together as one
higher-level feature commit.
An explicit request to squash commits authorizes this workflow: choose the grouping and proceed without a separate plan-approval step. Ask only when materially different groupings or scope choices are genuinely a toss-up after reading the diffs, or when the user explicitly requested a plan for review. Merely discussing or editing this skill does not authorize a squash. It never touches the branch ref until the very last step, and only ever operates on unpushed commits. It creates no backup tags or stashes; the original tip is recoverable from the branch reflog after the verified ref move.
Usage
/squash-commits # aggressive feature-level grouping of unpushed commits
/squash-commits --conservative # only fold obvious WIP/fixup commits into their parent
/squash-commits origin/main # use an explicit base instead of auto-detecting unpushed rangeHard rules
- Never rewrite pushed commits. Only squash commits not present on any remote.
- Never reorder commits. Groups are contiguous runs in original order. Each new commit is the snapshot at its group's last commit, so a non-contiguous group would silently absorb every commit between its members.
- The final tree must be byte-identical to the original tip before the branch is moved. Squashing changes history, never content. Verify this and abort if it differs.
- Leave the working tree and index alone. Uncommitted and untracked changes are fine: the rebuild never touches them, and the final tree matches the original tip, so they stay exactly as they were. Never stash, reset or check out to clear them.
- No interactive rebase.
git rebase -iis unsupported in this harness. Use the rebuild method below. - Leave no persistent helper state. Do not create backup tags, branches, or stashes.
- Leave no temporary files. Use subject-only commit messages, so no message files are needed. Git's normal reflog and unreachable-object retention are not helper state and can be left to normal expiry/GC.
Workflow
Step 1: Preconditions
git status --porcelain # note the output (STATUS_BEFORE); Step 7 compares against it
git rev-parse --abbrev-ref HEAD # current branch (save as BRANCH)Step 2: Determine the unpushed range
Find the commits to squash and the base (the commit they sit on top of).
- If a base ref is passed as an argument (e.g.
origin/main, a SHA):BASE= that ref. Range =BASE..HEAD. - Otherwise auto-detect unpushed commits:
This lists commits reachable from HEAD that are on no remote-tracking branch (newest first). The oldest of these is the last line;git rev-list HEAD --not --remotes --onelineBASE= its parent (<oldest>^).- If the list is empty → nothing to squash. Tell the user and stop.
- If it includes the root commit (no parent), or there are no remotes at all,
auto-detection is unreliable — ask the user for an explicit base ref (e.g.
origin/main) and re-run, or confirm a base before continuing.
Save ORIG_TIP = git rev-parse HEAD.
Check for merge commits in the range:
git rev-list --merges BASE..HEADIf non-empty, stop: this skill only handles linear history. Tell the user the range contains merge commits and squashing them is out of scope.
If the range has 0 or 1 commit, there is nothing to combine — say so and stop.
Step 3: Read and analyze the commits
List the range oldest→newest and read what each commit does:
git log --reverse --format='%h %s' BASE..HEAD
git log --reverse --stat BASE..HEAD # add -p for full diffs if grouping is unclearFor each commit note: its subject, the files it touches, and whether it's substantive work, a fixup of an earlier commit, tests, or docs.
Step 4: Choose the grouping
Partition the commits into contiguous groups, each becoming one new commit.
Default (aggressive, feature-level): collapse adjacent commits that serve one logical feature into a single commit — initial implementation + its follow-up fixes + its tests + its docs become one. The goal is the smallest set of commits that each stand on their own.
--conservative: only fold obvious checkpoint/WIP/fixup commits (e.g. "wip", "fix
typo", "address review", "oops") into the substantive commit they belong to. Leave
genuinely distinct logical commits as separate groups.
For each group, write a new message per the commit policy:
- Type is exactly one of
feat/fix/chore. No scopes, no parentheses — writefeat: add login button, notfeat(ui): ....feat— a user-noticeable addition.fix— a user-noticeable bug fix.chore— everything else (refactor, tests, docs, internal).- If a group mixes a feature and an unrelated fix, that's a sign they shouldn't be one group — split them.
- Omit the body. Use a single subject line so this workflow needs no temporary message files.
Briefly summarize the chosen groups and continue to Step 5 without waiting for approval. Routine message wording and clear feature/fixup boundaries are yours to decide.
If reading the diffs leaves a genuine toss-up between materially different groupings or scopes, present the concrete alternatives, recommend one, and ask the user to resolve that choice. Pause only for that decision; their answer authorizes proceeding without another confirmation. If the user asked only for a plan, present it and wait for approval.
Step 5: Preserve the recovery point without helper refs
Do not create anything. Keep the recorded ORIG_TIP: the original branch still points
there until Step 7, and the branch reflog records that tip when the ref moves. This gives
the user an undo point without a backup tag, branch, stash, or temporary file.
Step 6: Rebuild the history (commit-tree)
Determine each group's tip = the newest (last) original commit in that group, in order.
The last group's tip must be ORIG_TIP.
Build one new commit per group directly from its tip's tree, oldest group first. Each command prints the new commit's SHA, which is the parent of the next:
git commit-tree '<group1_tip>^{tree}' -p <BASE> -m '<group1-subject>' # prints N1
git commit-tree '<group2_tip>^{tree}' -p <N1> -m '<group2-subject>' # prints N2
# ...one command per group; the last SHA printed is NEWTIPEach new commit is exactly the snapshot at its group's tip, so nothing is merged and
nothing can conflict. Do not use git merge --squash for this: a squash merge records
no parent, so every group after the first merges against BASE and conflicts wherever it
edits lines an earlier group touched.
commit-tree touches no working tree, index, HEAD or ref; the new commits stay
unreferenced until Step 7. If a command fails, fix it and re-run from that group — there
is nothing to clean up.
Run one command per group with literal SHAs, copying each printed SHA into the next command. Do not drive the rebuild or the checks below with a shell loop, array, or word-split variable: zsh does not split unquoted variables, so a list of pairs arrives as one argument.
commit-tree runs no commit hooks. The content already passed them, but a commit-msg
hook enforcing a message format does not run, so each subject must follow the commit
policy on its own.
Step 7: Verify, then move the branch
Verify the content is identical before touching the branch:
git rev-parse '<ORIG_TIP>^{tree}' '<NEWTIP>^{tree}' # MUST print the same hash twice
git log --oneline <BASE>..<NEWTIP> # MUST show exactly the planned subjectsIf the tree hashes differ, the last group's tip was not ORIG_TIP — do not move the
branch. Report what diverged; the branch is untouched and nothing needs cleaning up.
If both checks pass, point the branch at the rebuilt history:
git update-ref -m 'squash-commits: <N> → <M> commits' refs/heads/<BRANCH> <NEWTIP> <ORIG_TIP>
git status --porcelain # MUST match STATUS_BEFOREupdate-ref with the old value moves the branch only if it still points at ORIG_TIP. If
it moved since Step 2 (a new commit, a pull), it fails with cannot lock ref … expected
<ORIG_TIP> and changes nothing — stop and re-plan from Step 2. HEAD's tree is
unchanged, so any uncommitted or untracked changes show exactly as they did in Step 1.
Step 8: Report
Show the result and how to undo:
git log --oneline <BASE>..HEAD## Done — squashed <N> commits into <M>
<new oneline log>
Original tip: `<ORIG_TIP>` (retained in `<BRANCH>`'s reflog).
- Undo: `git reset --soft <ORIG_TIP>` — restores the original commits and leaves the
index and working tree as they are.
- Cleanup: complete — no backup tag, branch, stash, or temporary message files remain.Git will normally retain the replaced commits through the branch reflog until reflog expiry and garbage collection. That is standard Git recovery history, not a leaked ref; do not expire reflogs or run GC as part of this skill.
Examples
Example 1: superhuman run left 6 WIP commits
/squash-commits
Auto-detects 6 unpushed commits via git rev-list HEAD --not --remotes. Reading them
shows two features: an export button (impl + 2 fixups + a test) and an unrelated config
tweak. Chooses Group 1 → feat: add CSV export button (5 commits) and Group 2 →
chore: bump lint config (1 commit). Without another approval step, builds two commits
with git commit-tree from the two group tips' trees, verifies the final tree matches the
original tip, and update-refs main onto the result — 6 commits become 2 with the
original tip retained only in the reflog.
Example 2: conservative, only fold fixups
/squash-commits --conservative
Range has feat: add auth, fix typo, address review, refactor session store,
feat: add logout. Folds fix typo and address review into feat: add auth; leaves
the refactor and the logout feature as their own commits. 5 commits → 3.
Example 3: explicit base
/squash-commits origin/develop
Skips auto-detection and squashes everything in origin/develop..HEAD, useful when the
branch has no upstream set or you want to bound the range to a specific integration point.
Troubleshooting
"Nothing to squash"
Cause: git rev-list HEAD --not --remotes is empty — all commits are already pushed,
or HEAD is at a remote-tracking branch.
Solution: Confirm there are local-only commits. If the branch has no upstream and no
remote contains the work, pass an explicit base: /squash-commits origin/main.
Auto-detection picks up too many commits (no remotes / new repo)
Cause: With no remote-tracking branches, --not --remotes excludes nothing, so the
range walks back to the root commit.
Solution: Pass an explicit base ref (e.g. /squash-commits main or a SHA) bounding
the commits you want to squash.
Verify step shows different tree hashes
Cause: The last commit-tree did not use ORIG_TIP's tree — a group tip was
misidentified, or the plan dropped the newest commit.
Solution: Do not move the branch; it was never touched, so there is nothing to undo.
Re-check each group's tip against git log --reverse --format='%h %s' BASE..HEAD and
rebuild from Step 6.
update-ref fails with "cannot lock ref"
Cause: The branch no longer points at ORIG_TIP — a commit landed or the branch was
reset after the range was read.
Solution: Nothing changed. Re-run from Step 2 so the plan covers the current tip.
Range contains merge commits
Cause: The unpushed history isn't linear. Solution: This skill is out of scope for merges. Tell the user; suggest they handle the merges manually or rebase to a linear history first.
User wants to undo after the squash
Cause: The new grouping/messages aren't what they wanted.
Solution: Use the ORIG_TIP printed in the completion report:
git reset --soft <ORIG_TIP>. The trees are identical, so this restores the original
history without touching the index or any uncommitted work. If the report is unavailable,
inspect git reflog <BRANCH> and identify the tip immediately before the squash. Then
adjust the plan and re-run.
Legacy squash-backup-* tags exist
Cause: Older versions of this skill intentionally left a backup tag after every run.
They did not create stashes.
Solution: Inspect them with git tag --list 'squash-backup-*'. Delete only tags whose
target you no longer need with git tag -d <exact-tag-name>. Current runs do not create
these tags.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

