Splitting a Giant Antigravity Diff Into Meaningful Commits
Are you committing the agent's 400-line diffs as a single blob? Here is a practical workflow, with scripts, for re-splitting an unreviewable bulk commit into one concern per commit.
One morning I asked Antigravity to fix a login issue, nothing more. What came back was a diff across 9 files and roughly 420 lines. As I started reading, I noticed that besides the authentication middleware fix, it had also tidied up type definitions, removed unused imports, and adjusted a few error message strings, all blended into one block.
Every change was reasonable. But if I sealed it with a single git commit -am "fix login", then later wanting to undo just the wording change would mean reverting the auth fix along with it. As a reviewer, reading 420 lines in one breath is more than my attention can hold. That diff ended up hanging in limbo for half a day until I split it apart.
Agents do not separate concerns as they write. Whatever can be solved at once, they write at once. That is exactly why it pays to keep, in your own hands, a step that re-splits the result into units of meaning, even when you work as an indie developer on a solo project. Today I want to share how I do that, along with the script I actually use.
Why a Bulk Commit Becomes a Burden Later
The problem with a giant commit is not just that it reads poorly. The deeper costs are these three.
First, the granularity of rollback becomes coarse. git revert works only at the commit level. When a feature change and a refactor live in the same commit, there is effectively no way to undo just one of them.
Second, review turns into a formality. Human attention is finite. Shown 400 lines at once, you read the first few dozen carefully and then drift into "this is probably fine" for the rest. I see this even in my own code: when I try to review everything at once, I miss more.
Third, git bisect loses its edge. When you binary-search for the commit that introduced a bug, a commit holding several concerns lets you find the culprit commit but leaves you unsure which change inside it is to blame.
Aspect
Bulk commit
Per-concern commit
Rollback
All or nothing
revert one concern at a time
Review
Latter half gets skimmed
Each one is small and focused
Root cause
bisect finds the commit, not the change
Culprit commit equals culprit change
History meaning
Vague, like "fix login"
Each commit states its intent
The ideal is one concern per commit. Yet asking an agent to write that way from the start is unrealistic, because concerns naturally tangle as a fix unfolds. So I make my peace with it: let it write freely, and split when committing.
What You Can Do Bare-Handed: git add -p
The foundation of splitting is the interactive staging that Git ships with. git add -p shows the diff one hunk (a chunk of change) at a time and lets you accept or reject each.
# Stage interactively, reviewing the working tree hunk by hunkgit add -p
At the accept/reject prompt, the keys I use most are y (include this hunk), n (skip it), s (split it smaller), and e (edit by hand). You pick up only the auth changes with y, set the wording changes aside with n, and commit the auth part first.
# After staging only the auth-related hunks, commitgit commit -m "fix(auth): correct SameSite attribute on the session cookie"# Re-stage the rest as the next concerngit add -pgit commit -m "refactor: tidy up auth middleware type definitions"
This bare-handed method is reliable, but processing 420 lines across 9 files one hunk at a time is hard work. Even with s, when related hunks are scattered across separate files you have to remember in your head which belong to the same concern. Letting Antigravity assist here is today's real subject.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Why one giant commit makes review and rollback painful, and how to design around it
✦A prompt with an output contract that classifies hunks by concern, plus the script that commits them one at a time
✦A per-commit build gate, and how to handle hunks that genuinely overlap
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
The key is not to let the agent do the committing itself. What you delegate is only the classification: "Into how many concerns can this diff be divided, and which concern does each hunk belong to?" The act of committing stays in your hands, in a verifiable form.
First, extract the diff in a machine-friendly shape.
# Capture all unstaged changes with file pathsgit diff > /tmp/work.diff# Add a list of the changed files toogit diff --stat
Then hand it to Antigravity with an explicit output contract. Rather than a vague request, fix the shape of the return value.
Classify the attached git diff into "concerns" that make sense as review units.Constraints:- Give each concern a short imperative title and a Conventional Commits type- Assign every hunk to exactly one concern (no overlaps, no omissions)- Order them: refactor -> fix -> tests -> wording/format- Keep interdependent hunks (the same spot in the same function) in one concernOutput only the following JSON, no prose:{ "commits": [ { "type": "refactor", "title": "...", "files": ["path:line-range", ...] } ]}
Fixing the output to JSON lets you pass it straight into the next step for machine processing. At first I asked, free-form, to "split it nicely," but the explanations only grew longer and were never reusable. Once I pinned the shape, it stabilized considerably.
Why specify the order "refactor -> fix -> tests -> format"? Because when a reviewer can read in this order, the intent of the change is easier to follow. Put the groundwork-laying changes first and the behavior-changing ones after. This mirrors the natural order in which a person writes code.
Bundle and Commit Based on the Classification
Once you have the classification, select and commit hunks one concern at a time. Full automation lets misassignments slip by, so I keep a semi-automatic rhythm: stage one concern, eyeball the contents, commit.
When concerns split along file boundaries, passing paths to git add is enough.
# Concern 1: refactor (tidy type definitions)git add src/types/auth.ts src/lib/session.tsgit diff --cached --stat # always eyeball what is stagedgit commit -m "refactor(auth): consolidate session types into one file"# Concern 2: fix (the actual bug)git add src/middleware/auth.tsgit diff --cachedgit commit -m "fix(auth): cookie not sent under SameSite=Lax"
When concerns mix within a single file, applying a partial patch with git apply --cached is the reliable route. Ask Antigravity for "a valid unified diff containing only the hunks that belong to this concern," then apply it.
# Save the partial patch the agent produced and apply it to the index onlycat /tmp/concern-3.patch | git apply --cachedgit diff --cached # confirm only the intended hunks landedgit commit -m "style: unify the login error wording"# Changes left in the working tree move to the next concern
git apply --cached leaves the working tree untouched and applies the patch only to the index (the staging area). If it does not apply, it tells you right then with error: patch failed, which prevents the accident of silently committing a broken partial apply.
A Per-Commit Build Gate
The scariest outcome of splitting is a state where "checking out one of the intermediate commits breaks the build." For instance, if you commit a type move first but the fix to its callers lands in a later commit, that first commit alone will not compile. Since git bisect checks out exactly those intermediate commits, this becomes a real problem.
So insert a minimal check right after each commit.
# A small gate that type-checks and lints the most recent commit's statenpm run typecheck && npm run lintif [ $? -ne 0 ]; then echo "This commit fails on its own. Revisit the hunk assignment."fi
You can also, after all splits are done, check out each commit in order and verify it. git rebase with --exec runs a command per commit.
# Run typecheck on each of the split commitsgit rebase -i HEAD~4 --exec "npm run typecheck"
If an intermediate commit fails, the rebase stops. At that point, reorder the hunks or pull a dependent change into the earlier commit so that each commit stands on its own. This is often where I first realize that "the type move and the caller fix were really the same concern." The verification of the split teaches me where the concern boundary actually lies.
Handling Interdependent Hunks
Splitting does not always go cleanly. The case I meet most is formatter spillover. The agent fixes one line, format-on-save shifts the indentation of the whole file, and the diff balloons. Here, rather than carving the formatting into a separate commit, it is cleaner in the end to revert the formatting once and keep only the essential change.
# For a file with heavy formatter spillover, revert and redo itgit checkout -- src/components/LoginForm.tsx# Have Antigravity rewrite only the essential change as a minimal diff
The other case is when two concerns overlap on the same spot of the same function. You physically cannot split that hunk. Do not force it; combine them into one commit and write honestly in the commit body that "this change includes X and Y." The goal of splitting is reviewability, not raising the commit count.
Situation
What to do
Concerns split by file
Split straightforwardly with git add <path>
Concerns mixed within one file
Partial apply with git apply --cached
Diff bloated by formatter spillover
checkout to revert, then rewrite
Two concerns physically overlap
Keep as one commit, state both in the body
A Small Prep Script to Keep on Hand
Finally, assembling this flow by hand every time is tedious, so I keep the diff capture and the classification request in one script. I deliberately leave the committing itself unautomated, because I want a visual check there.
#!/usr/bin/env bash# split-prep.sh — pull the diff and prepare the material for classificationset -euo pipefailOUT="/tmp/split-$(date +%s)"mkdir -p "$OUT"git diff > "$OUT/work.diff"git diff --stat > "$OUT/stat.txt"if [ ! -s "$OUT/work.diff" ]; then echo "No unstaged changes. Leave changes in the working tree first." exit 1fiecho "Wrote the diff to $OUT/work.diff ($(wc -l < "$OUT/work.diff") lines)"echo "Hand this to Antigravity and receive a per-concern JSON classification"echo "Then commit one concern at a time with git add / git apply --cached"
This script only prepares the diff as material; the judgment stays with the human. The speed of letting the agent write, and a granularity you can actually review. It is a modest device for not letting go of either.
When a giant diff comes back, do you seal it as-is, or split it apart first? That one extra step helps the version of you, three months from now, who wants to undo just that one change. Next time an agent hands you a large diff, try a single git add -p. Once the idea of re-splitting settles into your hands, your commit history starts working in your favor.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.