Git Stack Rewrite
Use this skill for history rewrites where staged changes or an existing source commit must be folded into an older target commit and branch stacks must be moved onto the rewritten replacement commit.
Supported prompt forms:
use git stack rewrite to squash commit [<source-commit>] into [<target-commit>]use git stack rewrite to fold the staged change into [<target-commit>]
Rules
- Treat this as destructive history rewriting even when it is local-only.
- Do not push unless the user explicitly asks.
- Never skip affected-branch discovery. Rewriting a base commit invalidates every branch on top of it, not only the branch currently checked out.
- In commit-to-target mode, the source commit must be reachable from the branch stack being rewritten, and normally must be a descendant of the target commit. Stop and explain if that ancestry is not true.
- Create backup refs before moving branch heads.
- Preserve user work. If the worktree has unrelated unstaged or untracked changes, report them and avoid touching them.
Workflow
Inspect state:
git status --short --branch- Record the starting branch with
git rev-parse --abbrev-ref HEAD; the workflow ends by switching back to it. - For staged-to-target mode:
git diff --cached --statandgit diff --cached --check - For commit-to-target mode:
git show --stat <source-commit>andgit diff --check <source-commit>^..<source-commit> git show --no-patch --format='%H%n%P%n%s' <target-commit>- For commit-to-target mode:
git show --no-patch --format='%H%n%P%n%s' <source-commit>
Identify branch topology:
- List requested branches and their heads with
git rev-parse. - Find all local branches affected by rewriting the target:
git branch --contains <target-commit> --format='%(refname:short)' - In commit-to-target mode, also find branches that contain the source:
git branch --contains <source-commit> --format='%(refname:short)' - Include every branch the user named.
- Also rebase other affected local branches when the user asked for all affected branches; otherwise report extra affected branches and ask before moving branch heads outside the user's stated scope.
- Determine stack order from nearest to farthest descendant of the target commit. Use
git log --graph --decorate --oneline --allorgit merge-base --is-ancestorchecks when needed.
- List requested branches and their heads with
Create backup refs:
- For each branch that will move, create a local backup branch such as:
git branch backup/rewrite-<target-short>-<safe-branch-name> <branch> - If the backup branch already exists, choose a unique suffix instead of overwriting it.
- For each branch that will move, create a local backup branch such as:
Prepare the change to fold into the target:
- Staged-to-target mode: create a temporary fixup commit from the staged changes:
git commit --fixup=<target-commit> - Commit-to-target mode: use the source commit directly as the fixup commit.
- Record the fixup/source commit id.
- Staged-to-target mode: create a temporary fixup commit from the staged changes:
Rewrite the target commit:
git switch --detach <target-commit>git cherry-pick --no-commit <fixup-or-source-commit>git commit --amend --no-edit- Record the new replacement commit id.
Rebase branches in stack order:
- For staged-to-target mode, replay all commits above the old target while dropping the temporary fixup commit from step 4 (its patch is now in the new target); if the fixup commit sits on a branch being rebased, use the same interactive,
GIT_SEQUENCE_EDITOR-driven rebase as below and remove it from the todo. - For commit-to-target mode, replay commits above the old target while dropping the source commit because its patch is now folded into the new target. Prefer interactive rebase with
--rebase-mergesdisabled unless merge commits are part of the branch stack. Whenever-iis used, always drive it non-interactively viaGIT_SEQUENCE_EDITOR(or an explicit todo) after inspecting the commit list – a baregit rebase -iopens an editor and blocks in an agent context. - For a simple linear stack in commit-to-target mode, a safe pattern is:
- Create the new target as above.
- Rebase the nearest affected branch with
git rebase -i --onto <new-target> <old-target> <branch>and drop<source-commit>from the todo. - Rebase later stacked branches with
git rebase --onto <rebased-parent-branch> <backup-of-old-parent-branch> <branch>.
- For staged-to-target mode, the first branch above the target can use:
git rebase --onto <new-target> <old-target> <branch> - For each later stacked branch:
git rebase --onto <rebased-parent-branch> <backup-of-old-parent-branch> <branch> - If a branch was affected but is independent of the named stack, rebase it with the nearest correct old base from its backup topology. In commit-to-target mode, also ensure the source commit is dropped from that branch if present.
- For staged-to-target mode, replay all commits above the old target while dropping the temporary fixup commit from step 4 (its patch is now in the new target); if the fixup commit sits on a branch being rebased, use the same interactive,
Verify:
git status --short --branch- For each moved branch:
git merge-base --is-ancestor <new-target> <branch>must pass.git merge-base --is-ancestor <old-target> <branch>should fail unless an intentional side branch still contains it.- In commit-to-target mode,
git merge-base --is-ancestor <source-commit> <branch>should fail unless an intentionally untouched branch still contains it.
- Compare old and new tip trees when useful:
git diff --stat <backup-branch>..<branch> - Confirm the intended change is present in the rewritten target commit with
git show --stat <new-target>. - Switch back to the starting branch recorded in step 1 (
git switch <starting-branch>); never leave HEAD detached or on a branch the user did not start from.
Report:
- Old target commit id, source/fixup commit id, and new replacement commit id.
- The branch HEAD was returned to.
- New branch heads.
- Backup refs created.
- Any affected branches intentionally not rebased.
- Verification commands run and their result.
- Push implications: rewritten branches with remotes will need force-with-lease pushes if the user wants to publish them.
Staged-Only Shortcut
For staged-only changes with no dependents, steps 4–6 reduce to: fixup commit → detach/cherry-pick/amend → rebase the affected branches in stack order.