Make Agents Split Commits by Patch
When an agent bags a feature, a refactor, tests, and formatting into one commit, git bisect, review, and revert all get harder at once. The same “atomic commit” expectation you have for humans belongs in the agent’s Rule and Done definition: one commit = one logical patch.
This post covers only one-commit criteria · message format · mixed changes. agent-pr-body is PR body sections; agent-diff-review is reading diffs for review. Here the axis is staging and splitting commits. No pricing, plans, or affiliate links.
Grounded in git commit, git add —patch, Conventional Commits, and common atomic-history practice.
What counts as one commit?
One-line answer: A commit should carry one intent (a review / revert / bisect unit). Cut by logical patch, not “I touched the same file.”
Pass/fail criteria to put in the agent brief:
| Criterion | Pass | Fail (split) |
|---|---|---|
| Intent | One bug / one feature / one refactor | Feature + unrelated refactor + formatting |
| Build/tests | That commit alone is green (or team-allowed WIP) | Broken middle commits left behind |
| Review | Why is clear from message + diff | Reviewer guesses intent from file lists |
| Revert | Reverting that commit does not smash other work | Two features tangled in one SHA |
| Scope | Related tests/types/docs belong to the same intent | Drive-by cleanup in another module |
Prompt line:
One commit = one logical patch (review/revert/bisect unit).
Do not mix feature, unrelated refactor, and formatting in one commit.
If the working tree has multiple intents, split commits before finishing.
Avoid:
- End-of-session
git add -A && git commit -m 'wip'— history loses recovery units. - Implementation without tests, tests in the next commit — unless the team documents WIP/
fixup!rules, prefer tests in the same intent for bisect (follow team policy). - Whole-tree formatter noise inside a feature commit — put formatting in a separate commit or pre-commit.
You do not need “one file = one commit.” Several files for one intent → one commit. Two intents in one file → split with git add -p.
What message format?
One-line answer: Require a subject (about 50–72 chars) + body (why and scope). If the team uses Conventional Commits, lock type(scope): summary. Ban subjects that are only “update”, “fix”, or a file dump.
Git opens an editor for the message; convention is a summary line, blank line, then body. Conventional Commits define types such as feat / fix / docs / refactor / test / chore, optional scope, and BREAKING CHANGE in the body.
Minimal agent template:
<type>(<optional-scope>): <imperative summary ≤72 chars>
Why: <one or two sentences>
Scope: <what changed / what did not>
Test: <commands you actually ran, or "n/a: docs-only">
Example (conceptual):
fix(auth): reject expired refresh tokens at gateway
Why: clients with stale refresh kept getting 500 from upstream.
Scope: gateway middleware + unit tests; no public API change.
Test: go test ./internal/gateway/...
Rules:
- Imperative, outcome-focused — prefer “reject expired refresh tokens” over “fixed bug.”
- Test lists only commands that ran — if not run, omit and fail Done (agent-test-gate).
- Issue IDs — only if the team uses them (
Refs: ENG-1234or a title prefix). Do not invent IDs. - Trailers (
Co-authored-by/Generated-by) — only when team policy says so. Never put secrets in messages. - amend / force — forbid
--amend/push --forceon already-pushed commits without a human instruction (spell this in the Rule).
If the repo has a template, point the agent at .gitmessage or that path instead of reinventing the format in every chat.
How to handle mixed changes?
One-line answer: If the working tree has more than one intent, do not commit once. Stage by hunk or path (git add -p or equivalents), then commit in intent order.
Checklist for Done:
[ ] git status / git diff — list distinct intents
[ ] For each intent: stage only related hunks (git add -p PATH or pathspecs)
[ ] git diff --cached — confirm staged set matches ONE intent
[ ] Commit with the message template for that intent
[ ] Repeat until clean (or report leftover out-of-scope files)
[ ] Never git add -A when multiple intents are present
Tool notes:
| Situation | Method | Caution |
|---|---|---|
| Hunk-level | git add -p / git add -p -- path | Interactive; non-interactive agents need path/partial staging |
| File-level enough | git add path1 path2 | Fails if one file holds two intents |
| Unstage | git restore --staged -- path | Fix mistakes before commit |
| Already committed as one bag | Soft reset and re-split per human policy | Do not rewrite pushed history lightly |
| Format noise mixed in | Separate format commit or pre-commit | Keeps feature diffs readable |
Non-interactive agents may not drive git add -p prompts. Then: (1) edit in intent order so files do not mix, (2) use apply/restore-patch or editor staging APIs, or (3) Rule: if mixed, do not commit—ask a human to split.
Upstream habits that reduce mixing:
- Write an intent list first; before commit, match it to
git diff. - Keep auto-format / import churn out of the feature commit.
- Generated code and lockfiles only in team-allowed scope; unrelated bumps are separate commits or out of scope.
Adjacent: PR Summary belongs in agent-pr-body; teaching the agent to read diffs before review belongs in agent-diff-review.
One-line wrap-up: One intent = one commit → type(scope): summary + why/tests → if mixed, split with add -p (or equivalent); forbid git add -A.
FAQ
Will “atomic” mean tiny noisy commits?
Split at boundaries that matter for review and bisect—not “one line = one commit.” Cut where a revert would still make sense.
We do not use Conventional Commits?
A one-line subject plus Why/Test in the body is enough. Align type prefixes with team docs; still ban “update” / file-list-only subjects.
The agent already ran git add -A?
git restore --staged, then restage by intent. If it already committed and pushed, do not rebase/amend without human approval.
What about fixup / squash?
Teams that squash before merge may allow fixup! / squash! locally. Put the final history shape (how many commits survive, what messages) in the agent Rule.
Sources
- git-commit — messages and commit units
- git-add —patch — hunk staging
- Conventional Commits — type(scope): summary
- Pro Git · Recording Changes — staging and commit basics
- Adjacent: agent-pr-body, agent-diff-review, agent-test-gate