Transform a branch with messy commit history into atomic, logical commits that are easy to review. Each atomic commit should represent one coherent change that builds on the previous state.
- User mentions "atomic commits", "break down commits", "restructure commits"
- User wants to "make commits easier to review" or "reorganize commit history"
- User asks to "split up commits" or "rewrite git history"
- User has a branch they want to clean up before creating a PR
First, gather information about the branch. Detect the base branch (main or master) by running git rev-parse --verify main / master; if neither exists, ask the user before proceeding.
# List of commits on the current branch (not on base)
git log --oneline <base>..HEAD
# Detailed view of what changed
git log -p <base>..HEAD
# Full diff of all changes
git diff <base>...HEADLook for natural boundaries:
- By file/module: Changes to different files or modules
- By concern: Refactoring vs. new features vs. bug fixes vs. tests
- By dependency: Changes that depend on each other vs. independent changes
- By type: Configuration, setup, implementation, tests, documentation
Typical atomic commit structure:
- Setup/infrastructure changes (dependencies, config)
- Core implementation (one feature at a time)
- Related tests for each feature
- Documentation updates
- Cleanup/refactoring
Use git rebase -i <base> to restructure. The strategy depends on current state:
If commits are already somewhat logical:
- Reorder commits to group related changes
- Squash related commits together
- Split commits that do too much
If commits are messy (e.g., "WIP", "fix", "more changes"):
- Squash all commits into one:
git reset --soft <base> && git commit -m "WIP: all changes" - Then split that single commit into logical pieces (see step 4)
To split a commit into atomic pieces:
# Start interactive rebase
git rebase -i <base>
# Mark the commit to split with 'edit'
# When rebase pauses, reset to the previous commit
git reset HEAD^
# Stage and commit changes piece by piece
git add -p # Interactively stage hunks
# or
git add specific/file.py
git commit -m "Clear, atomic commit message"
# Repeat until all changes are committed
git add .
git commit -m "Final piece"
# Continue the rebase
git rebase --continueEvery atomic commit message must follow Conventional Commits format. This repo has a conventional-commit hook that will reject non-conforming messages, and PRs are squash-merged using the PR title — so consistent commit hygiene matters at both layers.
Format: <type>(<optional scope>): <imperative summary>
<type>is one of:feat,fix,refactor,perf,docs,test,chore,ci,build,style,revert<scope>is optional and short (e.g.auth,coding-agents,rate-limit). Use it when it sharpens meaning; omit otherwise.- Summary is lowercase, imperative ("add", "fix", "drop" — not "added", "fixes"), no trailing period, ideally under 60 chars.
- Add a
!after type/scope (e.g.feat(api)!: drop legacy /v1 endpoints) only for breaking changes.
Pick the type from the actual change, not from filenames alone:
feat: user-visible new capabilityfix: bug fixrefactor: behaviour-preserving code changeperf: performance improvementdocs: docs onlytest: tests onlychore: tooling, deps, housekeepingci: CI/CD config onlybuild: build system / external depsstyle: formatting / whitespace onlyrevert: reverts a previous commit
Beyond the prefix, the message should:
- Describe what and why, not how
- Be understandable without seeing the code
- Reference broader context in a body paragraph if needed (blank line after the summary)
Examples drawn from this repo:
feat: skills layer — lazy MCP fetch with nudge-based progressive disclosurerefactor(coding-agents): drop list_hooks/get_hook MCP toolsfix: project-key lookup against symbol-keyed content_pathsdocs: skills layer + a decision guide for picking the right primitive
Avoid:
- "Update code", "WIP", "more changes"
- Past tense ("Added", "Fixed") — use imperative
- Mixing types ("feat: add X and fix Y" — split into two commits)
- Skipping the prefix on purpose — the hook will block the rebase
# 1. Confirm the current branch
git branch --show-current
# 2. Analyze the current commits
git log --oneline <base>..HEAD
git diff --name-status <base>...HEAD
# 3. Propose a breakdown to the user (logical groupings)
# 4. Create the atomic structure
git rebase -i <base>
# or
git reset --soft <base>
git reset # Unstage everything
# 5. Build commits one by one
# Use git add -p for fine-grained control
# or git add <specific files> for file-level commits
# 6. Verify the result
git log --oneline <base>..HEAD
git diff <base>...HEAD # Should show the same total changes as before- Single Responsibility: Does one thing and does it completely
- Builds: Each commit should leave the codebase in a working state
- Tests Pass: All tests should pass after each commit
- Self-Contained: Can be understood and reviewed independently
- Logical Size: Not too big (overwhelming) or too small (trivial)
- "WIP", "temp", "fix" commits → Squash into meaningful commits
- One giant commit → Split by concern/file/feature
- Mixed concerns (feature + unrelated refactor) → Separate
- Test and implementation in different commits → Group together
- Commits that break the build → Reorder or squash
Order commits so each builds on the previous:
- Dependencies/setup (if any)
- Core types/interfaces/models
- Implementation (one feature at a time)
- Tests (grouped with their feature or in a follow-up commit)
- Integration/wiring
- Documentation
This lets reviewers:
- Review one concept at a time
- Understand dependencies before seeing usage
- Verify tests match implementation
- Skip around without confusion
- Create a backup branch before starting:
git branch backup-<branch-name> - Use
git reflogif you need to undo a rebase - Verify the final diff matches the original:
git diff <base>...HEADshould show the same total changes - Force push only with
--force-with-lease, never--force - Never rebase commits that have been merged or that others are working on
Guide the user through the restructuring process with:
- Analysis of current commits and changes
- Proposed atomic commit structure (wait for user confirmation before rewriting history)
- Step-by-step commands to execute
- Verification that the restructure preserves all changes
- Final commit messages for each atomic commit