Agentic Git Workflows: Atomic Commits From Noisy LLM Diffs

Agentic Git Workflows: Atomic Commits From Noisy LLM Diffs

LLM agents love one giant “fix everything” diff: formatter churn, renames, logic fixes, and a mystery dependency bump in the same tree. That shape destroys git bisect, overwhelms CODEOWNERS, and makes revert a coin flip. Senior teams keep the agent for speed — then force an atomic commit workflow so humans still own history.

⚡ TL;DR: Never commit the raw agent working tree as one SHA. Classify hunks into intent buckets (tests → types → impl → chore), open stacked commits or PRs, and reject mixed concerns. Automate split suggestions; require humans to confirm ordering. Illustrative target: median AI-assisted PR ≤ 4 commits, each green on package tests, bisect-friendly within one business day.

Why noisy diffs become production debt

A single agent session often touches:

  • Unrelated import reorders / prettier
  • A real bugfix
  • Opportunistic renames
  • Lockfile churn

Reviewers rubber-stamp, then three weeks later bisect lands on a 1,800-line SHA that “also renamed the logger.” Atomic commits are not aesthetics — they are incident tooling.

✅ Tests-first ordering so each commit explains why.
git commit -am "wip agent" on a dirty monorepo.

Classify hunks before you stage

// classifyHunks.ts — illustrative intent tagging
export type Intent = "test" | "types" | "impl" | "chore" | "deps";

export function classifyPath(path: string): Intent {
  if (/\.(test|spec)\.[tj]sx?$/.test(path) || path.includes("__tests__/")) return "test";
  if (path.endsWith(".d.ts") || path.includes("/types/")) return "types";
  if (path.endsWith("package.json") || path.endsWith("pnpm-lock.yaml")) return "deps";
  if (path.includes(".github/") || path.includes("eslint") || path.includes("prettier")) return "chore";
  return "impl";
}

export function bucketByIntent(files: string[]): Record<Intent, string[]> {
  const out: Record<Intent, string[]> = { test: [], types: [], impl: [], chore: [], deps: [] };
  for (const f of files) out[classifyPath(f)].push(f);
  return out;
}
# After agent finishes — never commit yet
git status --porcelain > /tmp/agent-status.txt
python3 tools/plan_atomic_commits.py --status /tmp/agent-status.txt --print-plan
# Example plan:
# 1. test: add regression for null invoice id
# 2. impl: guard InvoiceService.create
# 3. types: export InvoiceId brand
# 4. chore: eslint unused import cleanup (optional separate PR)

Tests-first commit ordering

Preferred sequence for bugfix / feature work:

  1. Failing test (or characterization test) documenting the bug
  2. Minimal implementation that makes it pass
  3. Types / API surface if public contracts change
  4. Chore only if necessary — else drop or separate PR
#!/usr/bin/env bash
# commit_atomic.sh — illustrative
set -euo pipefail
INTENT="$1"; shift
MSG="$1"; shift
git add "$@"
pnpm exec tsc -b --pretty false
pnpm test --filter "...[HEAD]" -- --reporter=dot
git commit -m "$(cat <<EOF
${INTENT}: ${MSG}

Made-with: agent-assisted
EOF
)"

Wire this into agent hooks so the model proposes a plan, but git commit only runs through your script. Align with Cursor Rules for TypeScript Monorepos so the agent already knows package boundaries before it sprays files.

Stacked PRs when one session spans packages

If the agent crossed package boundaries, do not force one PR:

PR #1 packages/contracts — type + OpenAPI (owners: platform)
PR #2 packages/payments — impl + tests (owners: payments)
PR #3 apps/web — consumer update (owners: web)

Use git rebase --onto or stacking tools, but keep each PR green alone. Review bots should see diff-scoped context (AI Code Review Bots).

Recovering from a bad mega-commit

# Soft-split without rewriting remote main (local branch only)
git reset --soft HEAD~1
python3 tools/plan_atomic_commits.py --from-index
# Re-commit in intent order, force-with-lease only on your feature branch

❌ Never force-push shared main to “clean agent history.”
✅ Teach agents to call plan_atomic_commits as a mandatory tool before git_commit.

For overnight mechanical refactors, prefer batch jobs that already emit per-shard PRs (Bedrock Batch Inference overnight refactors) instead of one interactive mega-diff.

Closing checklist

✅ Dos
– ✅ Classify paths into intents before staging
– ✅ Commit tests before implementation when fixing bugs
– ✅ Keep chore/formatter noise out of logic SHAs
– ✅ Stack PRs by CODEOWNERS package
– ✅ Tag agent-assisted commits for later audit

❌ Don’ts
– ❌ Don’t accept “one commit per agent session” as policy
– ❌ Don’t mix dependency major bumps into feature commits
– ❌ Don’t rewrite published main history to hide mess
– ❌ Don’t let the model run raw git commit -a
– ❌ Don’t skip package tests between atomic commits

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply