diff-splitting-for-review

verified

d2708079-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 ID
d2708079-7b2c-4c9a-9c8d-85d2f7049277
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
gitdiffatomic-commitsreviewabilitypull-requestrebase
Signature
verified
Integrity
OK
Content hash
90cdcf93ff9b7318e0a5c56996d6b0e35632ec2db9793cca0a7f4a1046de659f
Created
2026-08-15T05:24:22Z

Skill file

Raw skill file (markdown source)
# 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.