Context
A history rewrite needs the current branch (git branch --show-current), a clean working tree (git status --short), and the verified base branch from Step 1 before anything else. Only with the base resolved do the commit range (git log --oneline origin/<base>..HEAD) and the diff stat (git diff origin/<base>...HEAD --stat) mean anything; never guess the base from a fixed candidate list. Read these in one batch, reuse the results within the session, and report a failing command rather than assuming the state it would have shown.
Task
Rewrite the current branch's commit history with clean, narrative-quality commits. The final tree must be byte-for-byte identical to the current state — only the commits change, not the code.
This is a destructive rewrite of the current branch. The user will force-push the result over the existing remote history when ready.
Steps
Determine the base branch
- If
$ARGUMENTSis provided, use it as the base branch. - Otherwise, detect it: check for an open PR with
gh pr view --json baseRefNamefirst. If no PR exists, look at the remote tracking branch or fall back to whichever ofdevormainexists on the remote. - Confirm the base branch with the user before proceeding.
- If
Validate preconditions
- Ensure no uncommitted changes (
git status --porcelainmust be empty) - Record the current tree SHA for later verification:
git rev-parse HEAD^{tree} - Record the current branch name
- Ensure no uncommitted changes (
Analyze the full diff
- Study all changes between the current branch and the base branch
- Understand the final intended state as a whole — files added, modified, deleted, and how they relate
- Read changed files to understand the purpose and structure of the work
- Review the existing commit history to understand what was done, but don't feel bound by it — the whole point is to tell a cleaner story
Plan the commit storyline
- Break the implementation into self-contained logical steps — typically fewer commits than the original history
- Each commit should represent a coherent, functional stage of development. A reviewer reading the PR commit-by-commit should see a clear progression where each step builds naturally on the last.
- Strip out any dead ends, reverts, fixups, or back-and-forth from the original history. The rewritten history should read as if the implementation went smoothly from start to finish.
- Aim for the smallest number of commits that are each independently valid but maximally separable — each commit should compile and work on its own, but no commit should mix unrelated concerns. New infrastructure before migration, migration before removal. Never delete code that is still referenced in a later commit.
- Consider: what would a reviewer want to see first? What context do they need before the next piece makes sense?
Stop here. Present the proposed commit list — ordered, each with a one-line summary of intent — and wait for explicit confirmation before moving on. Do not create
_rewrite-tempor run any tree-mutating command until the user has approved the storyline. This is a destructive rewrite; the gate matters more than the time it costs.Rewrite the history
- Create a temporary branch from the branch's original merge-base, not from the current tip of
<base>:git checkout -b _rewrite-temp $(git merge-base origin/<base> <branch>). The tree-match check in step 6 fails if the base has advanced since the branch was created, because files outside the branch's own diff will differ. Rebasing onto the current base is a separate step you will typically perform after the history is clean. Offer to do so after the history rewrite is complete. - Recreate changes commit by commit following the planned storyline
- Each commit must:
- Introduce a single coherent idea
- Leave the codebase in a functional state — each commit should stand on its own as a reasonable checkpoint
- Have a clear commit message (short summary line + description body when warranted)
- Use
git commit --no-verifyfor intermediate commits. Pre-commit hooks may check things like tests or type coverage that depend on the full implementation being present.--no-verifyskips the hooks, not step 4's bar: every intermediate commit still compiles and works on its own.
- Create a temporary branch from the branch's original merge-base, not from the current tip of
Verify byte-for-byte equivalence
- After the final commit, compare the tree SHA against the one recorded in step 2:
[ "$(git rev-parse HEAD^{tree})" = "<saved-tree-sha>" ] && echo "MATCH" || echo "MISMATCH" - If they differ, diff the two trees to find the discrepancy and fix it before proceeding.
- Run the final commit without
--no-verifyto ensure all checks pass on the complete state.
- After the final commit, compare the tree SHA against the one recorded in step 2:
Move the branch
- Point the original branch at the rewritten history:
git checkout <original-branch> git reset --hard _rewrite-temp git branch -d _rewrite-temp
- Point the original branch at the rewritten history:
Report
- Show the new commit log:
git log <base>..HEAD --oneline - Remind the user that force-push is needed to update the remote:
git push --force-with-lease - Do not push automatically — the user decides when.
- Show the new commit log:
Rules
- Commit authorship follows GLOBAL.md: single authorial point of view, no AI attribution or
Co-Authored-Bylines - The final tree SHA must exactly match the original — this is the only correctness check that matters
- Do not open a pull request — that is a separate workflow
- Do not force-push — only the user does that