git-history-surgery
Safely split, reorder, or rewrite already-pushed commits using a throwaway worktree. Isolates the surgery from any existing checkout, verifies byte-identical tree before force-pushing, and uses --force-with-lease to refuse silent clobbers from concurrent work. Use when asked to split a pushed commit into atomic pieces, fix a bulked commit after the fact, or reshape recent history on a shared branch.
Best use case
git-history-surgery is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Safely split, reorder, or rewrite already-pushed commits using a throwaway worktree. Isolates the surgery from any existing checkout, verifies byte-identical tree before force-pushing, and uses --force-with-lease to refuse silent clobbers from concurrent work. Use when asked to split a pushed commit into atomic pieces, fix a bulked commit after the fact, or reshape recent history on a shared branch.
Teams using git-history-surgery should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/git-history-surgery/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How git-history-surgery Compares
| Feature / Agent | git-history-surgery | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Safely split, reorder, or rewrite already-pushed commits using a throwaway worktree. Isolates the surgery from any existing checkout, verifies byte-identical tree before force-pushing, and uses --force-with-lease to refuse silent clobbers from concurrent work. Use when asked to split a pushed commit into atomic pieces, fix a bulked commit after the fact, or reshape recent history on a shared branch.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Git History Surgery
## When to invoke
Trigger on any of:
- "split this commit into smaller ones" (after a push)
- "rebase this into atomic commits"
- User points out a bulk commit and asks to fix history
- You bulked a multi-concern change into one commit and caught it before/after pushing
## Why a throwaway worktree
Doing `git reset --soft HEAD~1` in an *existing* worktree is risky:
- Another session might be on the same repo in a different worktree/branch. Your reset doesn't affect them, but switching branches there will.
- If the worktree you're in isn't on the branch you think (e.g. ops/main worktree was on `fix/usage-async-load` in a past session), a reset rewrites the *wrong* branch.
- Existing worktrees may carry uncommitted state you'd lose.
**The safe pattern: fresh detached worktree at origin/<branch>, do surgery, push, discard.**
## The pattern
```bash
# 1. Create throwaway worktree at the exact commit you want to rewrite
TMPWT=$(mktemp -d -t history-split-XXXXXX)
git fetch origin
git worktree add --detach "$TMPWT" origin/<branch>
cd "$TMPWT"
# 2. Verify you're where you expect
git log -3 --oneline
# 3. Undo the commit(s) keeping working-tree content
git reset --soft HEAD~1 # or HEAD~N for multiple
git reset # unstage so you can stage atomic chunks
# 4. Reset the file(s) to the *pre-change* base, then rebuild with Edits
git checkout HEAD -- <path>
# 5. Apply atomic change → stage → commit → repeat (N times)
# Each commit = one visual / structural / behavioural concern.
# Use Edit tool for text files to avoid whitespace drift.
# 6. VERIFY byte-identical rebuild before force-pushing
git diff origin/<branch>..HEAD
# ^^ MUST be empty. If not, you've lost content. STOP and investigate.
# 7. Force-with-lease: refuses if someone pushed since your fetch
git push --force-with-lease=<branch>:<EXPECTED_SHA> origin HEAD:<branch>
# 8. Cleanup
cd ..
git worktree remove --force "$TMPWT"
```
## Critical rules
- **Always `--force-with-lease`, never `--force`** on shared branches. Pass the *expected remote SHA* so it refuses if concurrent pushes landed.
- **Always verify `git diff origin/<branch>..HEAD` is empty** before pushing. A non-empty diff means you lost or added content vs. the original bulked commit — stop and rebuild.
- **One commit per concern.** If you're splitting a README rewrite, commits should be: hero image, section rename, bullet rewrite, showcase, callout — not "docs: update README".
- **Ask before force-pushing to main/master/release.** Even with a lease, protected branches warrant explicit confirmation. For feature branches, proceed.
- **Never edit a commit that someone else authored** unless you have explicit permission — you'll lose their attribution.
## Typical commit split commit messages
Keep them honest about what each commit actually does:
```
docs(readme): replace broken demo.gif with live dashboard hero
docs(readme): add usage analytics hero below the dashboard
docs(readme): rename section to "Terminal UI + CLI"
docs(readme): expand feature highlights with multi-provider + analytics
docs(readme): replace broken screenshot block with 6-panel showcase
docs(readme): add dedicated CLI section with command overview
```
Each message must stand alone — don't write "part 2 of README split" style.
## What this is NOT for
- Merged PRs: don't rewrite history after a merge unless everyone downstream agrees.
- Protected branches without lease authority: don't try.
- Code changes that concurrent sessions might depend on: let the bulk commit stand, fix going forward.
## Failure recovery
If `git diff origin/<branch>..HEAD` is non-empty:
1. Don't push. `git log` to see what's different.
2. Most likely you missed applying part of the final state, or introduced whitespace drift by rewriting via `cat <<EOF` instead of Edit.
3. Fix the drift, re-verify, then push.
If `--force-with-lease` rejects:
1. Someone pushed to the branch since your fetch. Your rewrite is now stale.
2. `git fetch origin`, rebase your atomic commits onto the new tip, re-verify diff, push again with the new lease SHA.
## Why this matters
Shared branches with force-push capability are one shell-slip away from catastrophic history loss. The throwaway-worktree + lease + verify-diff pattern makes the operation idempotent: if any step fails, nothing bad has happened yet. Only the final push is destructive, and the lease gates that.
## Recipe: Swap a squash-merge for a true merge commit (post-merge correction)
**Scenario:** A PR was already squash-merged but it should have been a merge commit (e.g., the project prefers to preserve atomic per-concern commits in main's history). The squash commit sits on the default branch.
**Trick:** the squash commit's tree IS the merged result. Reuse it. Build a new commit object that points to the same tree but has two parents (pre-squash main tip + feature branch tip). Force-push to swap. No actual re-merge happens, so there's no chance of conflicts and no working-tree changes.
```bash
# 1. Identify SHAs
SQUASH=$(git rev-parse origin/main) # current main tip (the squash)
PRE_SQUASH=$(git rev-parse $SQUASH^) # main before the squash
FEATURE=$(git rev-parse <feature-branch>) # feature branch tip (must still exist locally or on remote)
TREE=$(git rev-parse $SQUASH^{tree}) # merged tree (already correct)
# 2. Build the merge commit object via plumbing — no working tree touched
MERGE=$(git commit-tree $TREE -p $PRE_SQUASH -p $FEATURE \
-m "Merge pull request #N from <branch>" \
-m "<body>")
# 3. Sanity check — trees MUST match. If they don't, abort.
[ "$(git rev-parse $SQUASH^{tree})" = "$(git rev-parse $MERGE^{tree})" ] \
|| { echo "TREE MISMATCH — abort"; exit 1; }
# 4. Force-push with lease scoped to the squash SHA
# Refuses if anyone else has moved main since the squash.
git push --force-with-lease=main:$SQUASH origin $MERGE:refs/heads/main
# 5. Verify
git fetch origin main
git log --pretty=format:"%h %p %s" -1 origin/main # should show two parents
```
**Why this works:** `git commit-tree` is plumbing that creates a commit object pointing at any tree with any parents, no merge driver involved. Since the squash already produced the correct merged tree, you're just relabeling that tree as a merge commit with the right parentage. The byte-identical-tree assertion is the safety check that proves you haven't lost or added content.
**When to use:**
- Project convention is merge-commits, but a squash slipped through.
- You want to preserve the feature branch's atomic commits as parents reachable from main.
- The squashed PR landed within the last few commits and nothing else has been built on top.
**When NOT to use:**
- Other commits have landed on main on top of the squash (`origin/main^` is no longer the pre-squash SHA). The lease will reject anyway, but the operation is no longer simply reversible — you'd need to rebuild the chain.
- The feature branch was deleted from remote *and* local, with no reflog. You can't reconstitute a merge without parent2.
- The squash combined changes from multiple feature branches (rare, but the recipe assumes 1:1).
**Branch protection:** Default-branch force-push usually requires admin override. Coordinate or use `gh api` with admin token if needed. Per `feedback_force_push_docs`, in this repo history-surgery on main is acceptable when the trees match and the lease holds.Related Skills
workflow
Guide through structured delivery workflow with plan, implement, validate phases
webapp-testing
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
validate
Verify implementation against specifications
ui-ux-pro-max
UI/UX design intelligence. 67 styles, 96 palettes, 57 font pairings, 25 charts, 13 stacks (React, Next.js, Vue, Svelte, Astro, Nuxt, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui, Jetpack Compose). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient.
tui-style-guide
TUI style guide for consistent terminal interface design
token-usage
Show Claude Code token usage across sessions — daily, weekly, per-project, and per-session breakdowns. Parses {{HOME_TOOL_DIR}}/projects/**/*.jsonl for consumption data. Use when the user asks about token usage, costs, how many tokens were used, session statistics, or wants a usage report.
tmux-status
Show status of all tmux sessions including dev environments, spawned agents, and running processes
tmux-monitor
Monitor and report status of all tmux sessions including dev environments, spawned agents, and running processes. Uses tmuxwatch for enhanced visibility.
tmux-message
Reliable peer-to-peer message delivery to other Claude Code instances via tmux send-keys. Use as a fallback when claude-peers MCP send_message fails to surface in the receiver's inbox (delivered server-side but receiver never picks it up — observed behaviour). Also use when sending a directive to a known Claude Code TUI session by tmux session name or fuzzy hint, or when injecting a multi-line directive into a peer's prompt and submitting it. Trigger phrases — "claude-peers fallback", "tmux send-keys", "send to peer via tmux", "inject directive", "deliver to nanoclaw/hermes peer", "peer message". Tmux-only — won't reach peers running outside tmux.
test-driven-development
Use when implementing any feature or bugfix, before writing implementation code. Enforces RED-GREEN-REFACTOR cycle with test-first approach.
test-ainb
Run tests for the ainb (agents-in-a-box) Rust workspace via a 5-layer strategy — unit, insta snapshot, mock-plugin compositing, real-plugin spawn, vhs recording. Wraps cargo + insta + vhs into one CLI. Use when Stevie says "/test-ainb", "test ainb", "run ainb tests", "snapshot <component>", "regenerate vhs tapes", or any phrasing about validating ainb test layers. The skill autodetects which ainb-tui worktree the cwd sits in and dispatches to scripts/run.sh.
sync-learnings
Sync user-level agent config changes back to toolkit repository (works for Claude, Codex, Copilot)