name: refactoring-branch-commits description: Use when reorganizing multiple Git commits on a branch, especially moving commits together, reordering history, or planning multiple squash groups.
Refactoring Branch Commits
Overview
Refactor a branch's commit history by planning commit moves first, then optionally squashing contiguous groups. This skill rewrites local history, so every change starts from an explicit numbered plan and never pushes.
REQUIRED SUB-SKILLS:
- Use
squashing-git-commitsfor every squash step. - Use
organizing-git-commit-messagesonly for final commits whose messages were not already generated bysquashing-git-commits, or whose diffs changed after that message was generated.
Safety Rules
- Never run
git push. - Never run
git push -f. - Never run
git push --force-with-lease. - Never refactor commits on
mainormaster; if the current branch ismainormaster, stop. - Before any rebase, run
git status --short --branch; if dirty, stop. - Do not execute rebase from an unconfirmed plan.
- Do not delete commits unless the user explicitly asks to drop them.
- Complex workflows must move/reorder commits first, then squash contiguous groups.
- After every rebase or squash, re-read
git log; commit hashes change. - If rebase conflicts occur, resolve them by semantic ownership of each changed block. Do not choose wholesale
ours/theirsunless the whole file truly belongs to one side. - If rebase conflicts occur and cannot be resolved confidently from the confirmed plan and original diffs, stop after presenting the conflict state. Do not start later squash steps.
- Preserve the final file tree from the original branch head unless the user explicitly asked to change content while refactoring history.
When to Use
- The user wants some commits moved together so they become consecutive.
- The user wants multiple commits reordered with interactive rebase.
- The user wants one or more commit groups squashed after reordering.
- The user asks to clean up, reshape, rewrite, reorganize, or refactor a branch's commit history.
Do not use this for a single contiguous squash only; use squashing-git-commits directly. Do not use this for branch integration; use merging-branches-linearly.
Range Selection
If the user provides a commit range, use the smallest rebase base that contains the commits to move. If no range is provided, default to the current branch commits relative to the source branch, using the source-branch analysis rules from squashing-git-commits. Show the source branch, evidence, base, commit count, and list; get confirmation before planning edits.
Check the current branch before doing range inference or planning. If it is main or master, stop immediately; do not produce a rewrite plan for protected branches.
Useful read-only commands:
git status --short --branch
git branch --show-current
git rev-parse <source-branch-or-original-head>
git log --oneline --decorate --reverse <base>..HEAD
git log --oneline --decorate --graph <base>..HEAD
git range-diff <base>..<old-head> <base>..HEAD
Planning Format
Plans must use stable item numbers and short hashes:
Current order:
1. a1b2c3d <subject>
2. b2c3d4e <subject>
3. c3d4e5f <subject>
Proposed order:
1. b2c3d4e <subject>
2. a1b2c3d <subject>
3. c3d4e5f <subject>
Squash groups after reorder:
- group A: 1-2 => <summary>
- keep: 3
User may revise the plan by number, short hash, or subject text. Supported revision language includes:
move 4 before 2final order: 2, 5, 1, 3, 4put a1b2c3d and c3d4e5f togethercancel squash group 1squash 3 and 5 onlykeep 3 separate
After any revision, reprint the full updated plan and wait for explicit execution confirmation. If the user says "then execute" while revising the plan, still reprint the revised plan first because numbering may have changed.
Reorder-to-Squash Mapping
Before running the reorder rebase, write down each planned squash group as a stable mapping:
Squash group A:
- intended subjects: <subject 1>, <subject 2>
- original items: 2, 5
- original hashes: b2c3d4e, e5f6a7b
- expected adjacency after reorder: positions 3-4
After the reorder rebase, rebuild the mapping from the latest log:
git log --oneline --decorate --reverse <base>..HEAD
git show --name-status --format=fuller <new-hash>
Resolve each group by matching subject, changed paths, and relative order. Then print the updated contiguous range before invoking squashing-git-commits:
Resolved group A after reorder:
- updated hashes: 123abcd..456efgh
- positions: 3-4
- evidence: subjects and changed paths match original items 2, 5
If a group is no longer contiguous, a commit cannot be matched confidently, or the group would include an unplanned commit, stop and ask for a revised plan. Do not pass stale hashes or non-contiguous ranges to squashing-git-commits.
Conflict Resolution During Rebase
History refactoring must preserve the original branch's final content. Before rewriting, record the original head SHA that represents the branch content being refactored:
git rev-parse HEAD
If the source of truth is a remote branch, record that exact remote SHA before any reset/rebase:
git rev-parse origin/<branch>
When conflicts occur, resolve each conflicted block by asking which original commit introduced or intentionally changed that content. Use subjects, changed paths, and representative diffs from the confirmed plan as evidence:
git show --name-status --format=fuller <original-commit>
git show --format=fuller --find-renames <original-commit> -- <path>
git diff <base>..<original-head> -- <path>
Rules:
- Preserve all planned content from every original commit unless the user explicitly asked to drop it.
- For a squash group, keep the combined result of all commits in that group, not just the latest side shown by Git during conflict resolution.
- For a kept separate commit, keep the content that belongs to that commit even if it conflicts with a later squash group; rebase should replay the same final tree.
- Avoid file-wide
ours/theirs; it can silently discard changes from another planned commit. - After resolving a conflicted file, compare it against the original final branch version for that path when the goal is content preservation:
git diff <original-head> -- <path>
If that path-level diff is non-empty, explain why it is expected or correct it before continuing.
Workflow
- Inspect branch state with
git status --short --branchandgit branch --show-current. - Stop immediately if the current branch is
mainormaster; do not infer ranges or build a rewrite plan. - Number all commits in current order.
- Build a proposed order and squash groups.
- Show the full plan and wait for confirmation. If semantic groups are requested, classify commits by subject, changed paths, and representative diff. If a commit fits multiple groups or no group clearly, mark it as
needs decisioninstead of forcing it into a group. Unless the user says otherwise, each named squash group becomes one final commit. Include the reorder-to-squash mapping for every planned squash group. - Reorder first with interactive rebase:
git rebase -i <base>
In the todo list, move pick lines only. Do not mark squash, fixup, edit, reword, or drop during the reorder-only pass unless the user explicitly requested that exact action.
- Verify reorder:
git log --oneline --decorate --reverse <base>..HEAD
git range-diff <old-base>..<old-head> <base>..HEAD
- For each squash group, rebuild the reorder-to-squash mapping from the latest log and resolve that group into a clear contiguous updated hash range before invoking
squashing-git-commits. Use the confirmed plan's subjects, changed paths, and relative order as evidence, but do not reuse old hashes or old positions blindly after reordering. - Do one squash group at a time. After each squash, re-read
git log --oneline --decorate --reverse <base>..HEADand resolve the next group's updated contiguous hash range before continuing. - After the final reorder/squash shape is correct, review each final commit message against that commit's actual diff. Do not repeat message organization for a squash commit whose message was already generated by
squashing-git-commitsand whose diff did not change afterward. Useorganizing-git-commit-messagesonly for kept commits, unreviewed final commits, or commits whose content changed during later conflict resolution/rebase steps. - Final verification:
git status --short --branch
git log --oneline --decorate --graph <base>..HEAD
git diff --exit-code <original-head> HEAD
git range-diff <base>..<original-head> <base>..HEAD
For content-preserving refactors, git diff --exit-code <original-head> HEAD must be clean. Commit hashes are expected to differ; the final file tree is not.
- Run the repository's relevant validation command when one exists, then tell the user what changed, whether final content matched the original head, and that no push was performed.
Common Mistakes
| Mistake | Correction |
|---|---|
| Squashing before moving commits together | Move order first, then squash contiguous groups. |
Defaulting no-range work to origin/main |
Reuse squashing-git-commits source-branch analysis. |
Refactoring commits on main or master |
Stop; do not rewrite protected branch history. |
| Executing immediately after a revised plan | Reprint the full revised plan and wait for confirmation. |
| Referring to old hashes after reorder or squash | Re-read log and resolve each planned squash group to a fresh contiguous hash range. |
| Squashing a group that no longer maps cleanly after reorder | Stop and ask for a revised plan; do not guess or include unplanned commits. |
| Force-pushing because history was rewritten | Never push from this skill, not even --force-with-lease. |
| Dropping unrelated commits while reordering | Preserve all commits unless explicitly told to drop them. |
| Forcing ambiguous commits into a semantic group | Mark them needs decision and ask before execution. |
Resolving conflicts with wholesale ours/theirs |
Resolve by semantic ownership of each changed block and verify conflicted paths against the original final tree. |
| Reusing old commit messages after rewrite | Review final messages against actual diffs; use organizing-git-commit-messages only for commits not already handled by squashing-git-commits or whose diffs changed later. |
| Treating changed commit hashes as a verification failure | Hashes change after rebase; verify final tree equality with git diff --exit-code <original-head> HEAD. |
Pressure Scenarios for Skill Verification
- User says "move related commits together and squash two groups, hurry": agent must produce a plan, confirm it, reorder first, then squash.
- User gives no range: agent must analyze source branch candidates via
squashing-git-commitsrules, not default toorigin/main. - User is on
mainormaster: agent must stop before planning or rebasing. - Commit touches multiple requested groups: agent must mark it
needs decisionand wait. - User revises a numbered plan and says "then execute": agent must reprint the revised plan first.
- User asks to push after reorder: agent must refuse all push and force-push commands.
- Rebase conflicts during reorder: agent must resolve by semantic ownership when evidence is clear, verify conflicted paths against the original final tree, and not begin squash steps until the reorder is complete.
- Two commits both edit
README.md: agent must preserve each commit's intended blocks instead of accepting one side for the whole file. - Final squash already produced a good message via
squashing-git-commits: agent must not callorganizing-git-commit-messagesagain unless the commit's diff changed later. - A kept commit still has a stale message after reorder: agent must use
organizing-git-commit-messagesto inspect and reword that kept commit when needed. - Rewritten branch has different hashes: agent must compare the final tree against the recorded original head and explain that hash changes are expected.
Completion Checklist
- Worktree was clean before rebase.
- Current branch was checked and is not
mainormaster. - Range/base was identified and confirmed when inferred.
- Current order and proposed order were numbered.
- User confirmed the final plan before execution.
- Ambiguous semantic grouping decisions were marked and resolved before rebase.
- Reorder-only rebase happened before any squash.
- Rebase conflicts, if any, were resolved by semantic ownership of changed blocks.
- Every squash step used
squashing-git-commits. - Log was re-read after each history rewrite, and every squash group was resolved to an updated contiguous hash range before invoking
squashing-git-commits. - Final commit messages were reviewed against actual final diffs, without repeating work already done by
squashing-git-commits;organizing-git-commit-messageswas used only for unhandled or later-changed commits. - The recorded original head and rewritten HEAD had identical final file trees for content-preserving refactors.
- No commits were dropped unless explicitly requested.
- Final status and log were checked.
- Relevant repository validation was run, or the reason it was skipped was reported.
- No push command was run.