diff-splitting-for-review
verifiedd2708079-7b2c-4c9a-9c8d-85d2f7049277
Split a large change into small, reviewable, individually-committed diffs so reviewers (human or agent) can follow intent. Use before opening a PR or when a PR is too big to review.
Metadata
Skill file
# Diff Splitting for Review
Use before opening a PR, or when a PR is too large to review. A well-split diff
turns a monolithic "here is everything" into a story the reviewer can follow
one commit at a time.
## 1. The atomic-commit bar
Each commit must satisfy all three:
1. **One idea** — a single change you can summarise in a one-line subject.
2. **Builds and tests pass** — no intermediate commit is broken.
3. **Reviewable in isolation** — a reviewer can look at this commit alone and
understand it.
If a commit needs a paragraph of bullet points to describe, split it.
```text
Good: "Add User model + migration"
"Add password hashing utility"
"Add POST /login endpoint"
"Add auth middleware"
"Add login integration tests"
Bad: "Add login feature" (too big)
"Fix stuff" (vague)
```
## 2. Splitting techniques
### 2a. `git add -p` — the interactively staged commit
```bash
# Instead of git add ., stage patches one at a time
git add -p
# For each hunk: y (stage), n (skip), s (split further), e (edit)
# Build commits from the staged pieces
git commit -m "Add User model"
git add -p
git commit -m "Add hashing utility"
```
### 2b. Interactive rebase — reorder and squash
```bash
# After a messy implementation session:
git log --oneline # see the mess
git rebase -i HEAD~7 # interactive rebase the last 7 commits
# In the editor:
# pick a1b2c3d Add User model stub
# fixup d4e5f6g Fix typo in User model <- merge into above
# pick g7h8i9j Add password hashing
# squash j0k1l2m Tweak hash params <- merge into above
# pick m3n4o5p Add login endpoint
```
### 2c. `git reset --soft` + re-commit
```bash
# If all your work is committed but needs reorganization:
git reset --soft main # all changes back in the index, history gone
git commit -m "Add User model + migration" # first clean commit
git add src/hashing.py
git commit -m "Add password hashing utility" # second
# ... etc.
```
### 2d. Cherry-pick for cross-branch cleanup
```bash
# Extract specific commits from a messy branch to a clean one:
git checkout -b feature/login-clean main
git cherry-pick <commit-hash-for-model>
git cherry-pick <commit-hash-for-hashing>
```
## 3. Ordering commits — logical, not chronological
The commit order should tell a story, not a timeline:
1. **Foundations first** — data models, schemas, configs, new files created.
2. **Core behavior** — the algorithm, business logic, endpoint implementations.
3. **Integration + wiring** — connecting the new code to the rest of the system.
4. **Tests** — test each piece alongside it, or a final test commit.
5. **Docs / cleanup** — docstrings, README, CHANGELOG.
Chronological order ("the order I typed it in") produces commits like "wip",
"try something", "gah" — useless for review.
## 4. Verifying after splitting
```bash
# Every commit must pass:
git rebase main --exec "pytest -q" # run tests at each rewritten commit
# If any step fails, rebase stops — fix it there:
# fix the code
git add .
git rebase --continue
```
Do not push until `--exec "pytest -q"` succeeds at every commit.
## 5. Worked example: splitting a 3-concern branch
A feature branch has these changes mixed together:
```text
src/auth/token.py (JWT util)
src/auth/middleware.py (auth middleware)
src/auth/exceptions.py (custom exceptions)
tests/test_token.py (token tests)
tests/test_middleware.py (middleware tests)
README.md (doc update)
src/billing/pricing.py (unrelated price tweak — should NOT be here)
```
Split plan:
```bash
git checkout -b clean/auth-split main
# Concern 1: JWT util + its tests
git checkout messy-branch -- src/auth/token.py tests/test_token.py
git commit -m "Add JWT token creation + verify util"
# Concern 2: auth middleware + exceptions (depends on token util)
git checkout messy-branch -- src/auth/middleware.py src/auth/exceptions.py tests/test_middleware.py
# manually fix import for token.py if needed
git commit -m "Add auth middleware with custom exception types"
# Concern 3: docs
git checkout messy-branch -- README.md
# keep only the auth-related doc change, drop unrelated edits
git commit -m "Document JWT auth usage"
# The unrelated price tweak stays out entirely — it was scope creep.
```
The resulting branch tells a story: token util → middleware wiring → docs.
Every commit passes `pytest -q` independently.
## Guardrails
- Do **not** rewrite history on a shared branch (one others have pulled from).
Split before you push, or work on a cleanup branch.
- Do **not** leave intermediate commits broken — each commit must build and pass
tests independently.
- Do **not** squash everything into one commit to avoid splitting; that is a
different anti-pattern (the unreviewable monolith).
- Do **not** split beyond the atomic-commit bar — 20 one-line commits is noise,
not clarity.
- Do verify the split with `git rebase --exec "pytest -q"` before pushing.
## Pitfalls
- **Rewriting shared history** — if anyone else has pulled your branch, rewriting
with rebase causes duplicates and conflicts. Split before pushing.
- **Broken intermediate commits** — a `fixup` that leaves a commit in a broken
state makes bisect useless and confuses reviewers jumping between commits.
- **Squash-as-avoidance** — merging 15 messy commits into one "big commit" avoids
splitting effort but produces an unreviewable blob.
- **Over-splitting** — 40 commits for a 200-line change is not reviewable, it is
fragmentary.
- **Chronological order** — commits that follow the implementation timeline
("oops", "fix that", "undo", "actually") tell no story.
## Verify / Checklist
- [ ] Each commit passes all three atomic-commit checks (one idea, builds, reviewable).
- [ ] `git rebase main --exec "pytest -q"` succeeds at every commit.
- [ ] Commit order is logical (foundations → core → integration → tests → docs).
- [ ] No commit message says "wip", "fix", "tmp", or "cleanup".
- [ ] History was rewritten only on an unpushed or private branch.
- [ ] A reviewer can read the commits top-to-bottom and follow the story.
Attached files
No attached files.