Split large changes into logical commits by semantic meaning. Use when handling large features or refactors that should be split into focused commits.
Split uncommitted changes into a sequence of focused commits, each representing one logical concern. The goal is a commit history that tells a story a reviewer can follow โ and that git bisect and git revert can act on cleanly.
git status --shortgit diff HEAD --statSplit when changes span multiple concerns. A "concern" is a unit of intent: a bug fix, a feature, a refactoring, a dependency update. If the change fits in a single Conventional Commits subject line without "and", it's one concern โ commit it directly with formatting-commit instead.
Don't over-split. Three related files changed for one feature = one commit, not three. A rename touching 12 files = one commit. The question is always: "Would a reviewer want to see these together or separately?"
The injected state above shows which files changed. Read the full diff before grouping:
git diff HEAD
Understand what every change does and why before planning the split.
Identify distinct concerns and map files to each. Write the plan out before staging anything โ this catches ordering mistakes early and lets you reason about the whole sequence.
Consider:
git revert any single commit without breaking the others? If not, those changes belong together.When a file touches multiple concerns, put it in the dominant one. Clean file-level splits are more reliable than attempting per-hunk staging โ and almost always sufficient.
For each group, in dependency order:
git add <file1> <file2> ...
git diff --cached --stat
git diff --cached
After staging, invoke formatting-commit to compose the message and create the commit. Then move to the next group and repeat.
git log --oneline origin/main..HEAD
The log should read as a coherent narrative. If something looks off, git reset --soft HEAD~N and re-split.
| Situation | Commits | Why |
|---|---|---|
| Feature + its tests | 1 | Tests validate the feature โ they're the same concern |
| Feature + unrelated bug fix found along the way | 2 | Independent concerns with no dependency |
| Refactor + feature built on it | 2 (refactor first) | Feature depends on refactor; separating makes the refactor reviewable on its own |
| Bug fix + feature that exposed the bug | 2 (fix first) | Fix is independently valuable and may need to be cherry-picked |
| Dependency update + code adapting to new API | 1 | Inseparable โ the old code breaks without the update |
| Config + code using new config | 1 if tightly coupled, 2 if config is independently useful | Judgment call based on whether the config stands alone |
| Rename/move + logic changes | 2 (rename first) | Pure renames produce clean diffs; mixing in logic changes hides them |
chore commit, not two.