Splitting commits

Turn one messy set of changes into focused commits, safely.

Staged a bug fix, a dependency bump and some docs all at once? gitmit split proposes a separate commit for each:

$ git add -A
$ gitmit split

  Proposed commits (4)
  1. chore: bump version to 1.1.0
     package.json
  2. fix(auth): return null when no user is signed in
     src/auth/__tests__/login.test.ts
     src/auth/login.ts
  3. feat(billing): add refund support
     src/billing/pay.ts (2 changes)
  4. docs: add setup guide
     README.md

  ● What next?
  │ ● Create these 4 commits
  │ ○ Move a change to another commit
  │ ○ Edit a commit message
  │ ○ Cancel

How changes are grouped

gitmit breaks your staged changes into units. A unit is one hunk (a block of changed lines). A file that can't be split stays one unit: new, deleted, renamed and binary files, permission changes, and files with a single block of changes.

  • With an AI provider, the model groups units by intent: one commit per logical change, tests kept with the code they test, ordered so earlier commits don't depend on later ones. Its plan must use every unit exactly once. If it doesn't, the model gets one retry with the problems listed, and then gitmit falls back to offline grouping.
  • Offline, units are grouped by kind and area: build, dependency and tooling changes first, then one commit per area of code (with that area's tests), then docs, then CI.

Each commit then gets its own message, following the same rules as a normal gitmit run.

Adjusting the plan

Before anything happens you can:

  • Move a change to another commit, or into a brand new one. The affected messages are rewritten.
  • Edit a commit message.
  • Cancel. Nothing is changed.

Safety

Splitting rewrites what's in your staging area, so it is built to be safe:

  • Only the staging area and commits change. Your files on disk are never touched. Changes you haven't staged stay exactly as they are.
  • Rehearsed first. Before touching anything, gitmit replays the whole plan in a throwaway staging area and checks that the commits add up to exactly what you staged. If they don't, nothing happens.
  • Restored on failure. If anything fails partway, for example a pre-commit hook rejecting commit 2, gitmit moves your branch back and restores your staged changes exactly as they were.
  • Refuses to run mid-operation. It won't split during a merge, rebase, cherry-pick, revert or bisect.

Your normal commit hooks (pre-commit, commit-msg) run for every commit gitmit creates.

Limits: the smallest piece gitmit can move is one hunk; lines right next to each other can't go into different commits. If a pre-commit hook reformats staged files while committing (for example lint-staged with Prettier), a split may fail partway; when it does, everything is restored.