--- name: git-rewrite-history description: Rewrites git history safely with dry-run-first workflow. Supports commit amending, squashing, rewording, tag updates, and force pushing. Always proposes plan before execution. Triggers on keywords: rewrite history, squash commits, amend commit, fix commit, update tags, rebase history, clean history, fix tag project-agnostic: true allowed-tools: - Bash - Read - Edit - Write --- # Git Rewrite History Safely rewrite git history with mandatory dry-run planning and user confirmation. ## Critical Rules 1. **ALWAYS DRY RUN FIRST** - Propose plan, never execute without approval 2. **ALWAYS WAIT FOR CONFIRMATION** - User must explicitly approve before destructive operations 3. **ALWAYS CREATE BACKUP** - Backup branch before any rebase/amend 4. **ALWAYS SAVE TAG MESSAGES** - Preserve tag annotations before deletion ## Supported Operations | Operation | Use Case | |-----------|----------| | Amend Content | Change files in historical commits | | Squash Commits | Combine multiple commits into one | | Reword Message | Change commit message text | | Update Tags | Move tags to new commits after rebase | | Delete Branches | Remove local and remote branches | | Force Push | Push rewritten history to remote | ## Workflow ### Phase 1: ANALYZE Gather current state before proposing changes. ```bash # Current branch and HEAD git rev-parse --abbrev-ref HEAD git rev-parse --short HEAD # Commit history git log --oneline --decorate main # All tags git tag -l # All branches git branch -a -v ``` ### Phase 2: PLAN (DRY RUN) **OUTPUT FORMAT:** ```markdown ## DRY RUN - PROPOSED PLAN ### Current State - Branch: {branch} - HEAD: {sha} - Commits: {count} - Tags: {list} ### Operations 1. {operation description} 2. {operation description} ... ### Commands to Execute {numbered list of exact commands} ### Risk Assessment | Risk | Impact | Mitigation | |------|--------|------------| ### Affected - Commits: {list} - Tags: {list} - Remote: {yes/no force push required} **Awaiting approval to proceed.** ``` ### Phase 3: CONFIRM **STOP AND WAIT** for user to respond with approval (e.g., "OK", "proceed", "yes"). ### Phase 4: BACKUP ```bash # Create backup branch git branch backup/pre-rewrite-$(date +%Y%m%d-%H%M%S) HEAD # Save tag messages to temp files git tag -l --format='%(contents)' {tag} > /tmp/{tag}-message.txt ``` ### Phase 5: EXECUTE #### Pattern A: Amend Content in Historical Commit ```bash # Save cleaned file cp {file} /tmp/cleaned-file # Stash if needed git stash # Start interactive rebase (mark target as "edit") GIT_SEQUENCE_EDITOR="sed -i '' 's/^pick {short_sha}/edit {short_sha}/'" git rebase -i {parent_sha} # Apply changes when stopped cp /tmp/cleaned-file {file} git add {file} git commit --amend --no-edit # Continue git rebase --continue ``` #### Pattern B: Squash N Commits into 1 ```bash # For commits 2-N, change "pick" to "squash" GIT_SEQUENCE_EDITOR="sed -i '' '2,{N}s/^pick/squash/'" git rebase -i --root # OR for non-root: GIT_SEQUENCE_EDITOR="sed -i '' '2,{N}s/^pick/squash/'" git rebase -i {parent_sha} # When prompted for message, use edit mode: GIT_SEQUENCE_EDITOR="sed -i '' '1s/^pick/edit/'" git rebase -i --root git commit --amend -m "{new_message}" git rebase --continue ``` #### Pattern C: Reword Commit Message ```bash # Mark commit for edit GIT_SEQUENCE_EDITOR="sed -i '' 's/^pick {short_sha}/edit {short_sha}/'" git rebase -i {parent_sha} # Amend message git commit --amend -m "{new_message}" # Continue git rebase --continue ``` #### Pattern D: Delete Branches ```bash # Local branches git branch -D {branch1} {branch2} # Remote branches git push origin --delete {branch1} {branch2} ``` ### Phase 6: UPDATE TAGS After any rebase, commit hashes change. Tags must be recreated. ```bash # Get new commit hashes git log --oneline -{N} main # Delete old tags (local) git tag -d {tag1} {tag2} # Recreate tags on new commits git tag -a {tag} {new_sha} -F /tmp/{tag}-message.txt # OR with inline message: git tag -a {tag} {new_sha} -m "{message}" ``` ### Phase 7: FORCE PUSH ```bash # Delete remote tags first git push origin :refs/tags/{tag1} :refs/tags/{tag2} # Force push branch git push --force-with-lease origin {branch} # Push new tags git push origin {tag1} {tag2} ``` ### Phase 8: VERIFY ```bash # Final history git log --oneline --decorate -{N} main # Commit count git rev-list --count main # Tags git tag -l | sort -V # Remote sync git status ``` ## Output Format After completion, report: ```markdown ## Complete ### Final State {commit} (tag: {tag}) {message} {commit} (tag: {tag}) {message} ... ### Summary | Metric | Before | After | |--------|--------|-------| | Commits | {N} | {M} | | Tags | {list} | {list} | ### Verification | Check | Result | |-------|--------| | Commit count | {N} | | Tags exist | {list} | | Remote synced | Yes/No | ``` ## Safety Notes - `--force-with-lease` prevents overwriting others' work - Backup branches preserve original state for rollback - Tag messages saved to /tmp allow recreation with original annotations - Reflog preserves history locally (expires after 90 days by default)