Commit hygiene under an agent: described, reviewed, revertable
By the end you can
- Close the loop's fourth phase by committing rather than leaving work uncommitted
- Read the working-tree diff before a commit lands rather than after
- Say why git, not checkpointing, is the durable record of an agent session
- Commit at task seams so a single change can be reverted without unpicking the others
- Know which git operations the agent will refuse or be blocked from, and why
An hour in, the suite is green and the working tree holds nineteen changed files.
Some were edited by Claude's file tools. Two were rewritten by a sed one-liner
in a Bash call. One was touched by a subagent you asked to clean up imports. None
of it is committed, and you are about to start the next thing. The work is done
and it is not yet kept.
Commit is a phase of the loop, not paperwork
Lesson 4.1 laid out the four documented phases — explore, plan, implement,
commit. The fourth is not paperwork bolted on to the other three; the
best-practices workflow gives it an instruction shape of its own, commit with a descriptive message and open a PR. The agent can carry that out, because git is
one of the things it operates directly:
Claude Code works directly with git. It stages changes, writes commit messages, creates branches, and opens pull requests. — Overview
And it can describe what it committed, because your git state is part of what it can see — current branch, uncommitted changes, and recent commit history (How Claude Code works). So the mechanics are free. The judgement about when the commit happens and what goes in it is not, and that stays with you.
Read the diff first
There is a command for this, and it does not make you leave the session:
Run
/diffto look over the changes in your working tree without leaving Claude Code. You see the edits Claude has made so far alongside anything else you haven't committed. — Interactive mode
Alongside anything else you haven't committed. That is exactly the view you
want, because a commit takes the tree too, including the half-finished experiment
you made by hand forty minutes ago and forgot about. /diff is not only that
view: beside the tree view — labelled Current — the diff viewer carries a
turn view for each prompt after which Claude edited files, and Claude Code builds
those turn views from Claude's file edits rather than from git, so a change
Claude makes through a shell command appears only under Current
(Interactive mode). The edits
the next section shows rewind cannot restore are the same edits the per-turn view
does not show you. The docs file /diff under a stage of its own — Before you
ship — shared with /code-review and /security-review
(Commands). Here only the first of
the three matters; Lesson 11.5 takes /code-review.
Why checkpoints are not the record
Level 3 gave you /rewind, and it is good at what it is for. What it is for is
stated outright:
Checkpoints are designed for quick, session-level recovery. For permanent version history and collaboration, continue using version control, such as Git, for commits, branches, and long-term history. — Checkpointing
Lesson 3.4 listed what rewind does not restore and Lesson 11.4 builds the whole
channel table; two of those gaps decide this lesson. Files changed through the
shell — a mv, a sed -i, a codegen script — are outside the mechanism
entirely, and for any other subagent's edits the docs name git as the remedy
themselves. Those are precisely the two ways an agent makes a lot of edits
quickly, so the changes with the widest blast radius are the changes the undo
button does not hold. Nor do the snapshots that exist last: they are swept about
30 days after the session last saved one, which makes an uncommitted session not
archived work but work with an expiry date on its safety net.
Where the seams are
This section is this course's judgement rather than documented behaviour: a working rule, not a fact about the tool.
The trap is one sentence, typed at the end of a long session: "commit my changes." It is a perfectly good instruction and it produces a perfectly bad commit — the refactor, the bug fix the refactor enabled, and three incidental cleanups in one blob, with a message that honestly describes all of them and independently reverts none of them.
A commit is only useful later if it is a state you would want back. So commit at the points where the tree was in such a state. You already have those points:
| Seam | Why it is one |
|---|---|
| A step of the approved plan finished | The plan already argued this was a coherent unit |
| A check went from red to green | The tree just became provably better than it was |
| The agent is about to run a shell command that rewrites files | The files it rewrites land outside rewind's reach entirely; the commit is the only route back to what they said |
| You are about to delegate to a subagent | Same reason: for any subagent but a foreground forked skill, the docs send you to git |
| The next request changes subject | An unrelated change does not belong in the same revert |
Rows three and four are the ones people skip, and they are the ones that pay.
Committing immediately before a bulk shell edit costs you five seconds and buys
you git checkout as an undo for the exact class of change rewind cannot touch.
Describing a change you did not type
What the branch, the diff and the history cannot tell Claude is which of the nineteen files you consider the point. That is what you supply. The step-by-step form in the docs separates the two jobs — describe first, then ship:
summarize the changes I've made to the authentication modulecreate a prRead that first line as a review tool rather than a formality. A summary you did not write, of a diff you did not type, is the cheapest way to find out that the agent's account of what it did differs from yours — and if the summary needs the word "and" twice to be accurate, you are looking at two commits. The docs are explicit that the PR is yours to check as well:
Review Claude's generated PR before submitting and ask Claude to highlight potential risks or considerations. — Common workflows
What the agent will not do to your history
In auto mode a classifier reviews actions before they run, and several git operations are blocked by default (Permission modes):
| Operation | The documented reason |
|---|---|
git reset --hard, git checkout -- ., git restore ., git clean -fd, git stash drop, git stash clear | The classifier presumes they would discard uncommitted changes |
git commit --amend when the commit at HEAD was not created in this session | It is rewriting history the session did not make |
| Force push | Listed outright among the blocked categories |
Whether you are in that mode at all is a question about how the session started.
Auto mode became the built-in starting permission mode on Pro, Max and Team
plans in v2.1.228, and v2.1.233 on native Windows; "on earlier versions, the
built-in default is Manual". So on one of those plans, in a terminal or the VS
Code extension, the table above is usually live — but the plan is the last thing
consulted, not the first, and Lesson 10.1 walks the rows that can claim a session
before it ever reaches the plan. Plenty of sessions never do: claude -p and the
Agent SDK, an Enterprise plan or a Console API key, and the cloud-provider
sessions Lesson 4.2 lists all start in Manual.
In an interactive one that is the mode Lesson 4.2's press table puts two
Shift+Tab presses from plan mode, and the mode where a git reset --hard
reaches you as a permission prompt you have to read rather than as a classifier
block; in a -p run with no permission host there is nobody to read it, and the
request is denied instead. The difference is who is holding the line: where
there is a prompt it is you, one at a time, and a prompt approved by reflex
approves exactly the thing the table exists to stop. To opt into the table's
behaviour from one of those sessions, use claude --permission-mode auto, or
defaultMode: "auto" in ~/.claude/settings.json, or Shift+Tab in an
interactive session.
The amend rule has a second half, added in v2.1.198: --amend is also blocked
when the commit at HEAD has already been pushed, with one carve-out — a
message-only reword, --amend -m with nothing newly staged, on a commit Claude
created during this session. Read the two halves together and they are one rule.
Claude may amend what this session wrote and nobody else has seen; anything else
at HEAD is somebody's history.
Now read the table as one statement. Every entry guards something the session
cannot put back — uncommitted changes, or commits the session did not make.
Pushing is the entry that travelled the other way. A push to any branch of the
repository you are working in, the default branch included, is allowed by
default; that arrived in v2.1.211. Before it, only pushes to the branch you
started on, branches Claude created, and routine pushes to the default branch
were allowed by default, and before v2.1.203 any direct push to the default
branch was blocked outright. The relaxation is narrower than it sounds.
A non-default branch whose name marks it as a deploy or publication target, such
as production or gh-pages, is not covered: the classifier judges a push there
on its own terms. The push's content is still checked against every other rule on
the list, permissions.deny rules still block named branches in every mode, and
the remote's own branch protection still applies. These guardrails are not
suspicious of git; they are suspicious of losing things. Commit, and most of
the list stops being able to hurt you — there is nothing uncommitted left for
reset --hard to throw away.
Finding the session again from the PR
One more reason to end the loop with a commit and a PR rather than a tidy working tree: the conversation stays attached to it.
Claude Code links the session to the PR when Claude creates it with
gh pr createorglab mr create, and when Claude works on an existing PR. — Common workflows
Lesson 3.5 gave you claude --from-pr 1234 as a way into the session picker;
this is the other end of that thread. A committed, PR'd change carries a route
back to the reasoning that produced it. An uncommitted working tree carries a
route to nothing, and in about thirty days not even to its own snapshots.
The phase you can skip by accident
Exploration is the phase Lesson 4.1 said you skip without noticing. Commit is the
other one — skipped not by haste but by momentum, because the tests are green and
the next task is already interesting. Skipping exploration costs you a wrong
answer you can see; skipping commit costs you an hour of right answers you cannot
get back. Three moves, none of them a minute long: /diff the tree, ask for a
summary and check it against what you think happened, then commit at the seam you
are standing on.
Checkpoint
-
You ask Claude to reformat forty files with a
prettier --writeshell command, it goes wrong, and you pressEsctwice to rewind. What comes back, and what should you have done ten seconds earlier?Show answerAnswer
Not the forty files. Changes made through the shell are outside the checkpointing mechanism entirely — a
mv, ased -i, a codegen script, and aprettier --writeis exactly that class — so whatever else the rewind restores, the reformatting is not part of it. Those same edits are the ones/diff's per-turn views cannot show you either, because the turn views are built from Claude's file edits rather than from git; they appear only under Current.Ten seconds earlier you should have committed. That is the third seam in the table: the agent is about to run a shell command that rewrites files, so the commit is the only route back to what those files said, and it makes
git checkoutan undo for the exact class of change rewind cannot touch. Five seconds of typing against forty files. -
/diffshows a change to a config file that you are certain Claude never touched this session. Is the diff wrong?Show answerAnswer
No — that is
/diffdoing its job. It shows the edits Claude has made so far alongside anything else you haven't committed, which is the whole reason to run it before committing: the commit takes the tree too, including the half-finished experiment you made by hand forty minutes ago and forgot about.Two things to check before you trust your certainty, though. Claude may in fact have written that file through a shell command, which appears only under Current and never in a per-turn view. And which view you are reading matters: the diff panel's list skips test and generated files and collapses pre-session changes into one line, while the viewer's Current is your uncommitted changes from git — or, when there are none, what your branch adds on top of the default branch.
-
You are in auto mode and ask Claude to "clean up the working tree and start fresh". It reports that the command was blocked. Which rule caught it, and what does that block tell you about the state your repository is in?
Show answerAnswer
The first row of the blocked-operations table caught it:
git reset --hard,git checkout -- .,git restore .,git clean -fd,git stash dropandgit stash clearare blocked in auto mode because the classifier presumes they would discard uncommitted changes. "Clean up the working tree and start fresh" is a request for one of those, however pleasantly it is phrased.Read the block as a question about your repository: what exactly would that have thrown away? Whatever it is exists nowhere else, which is what the whole table guards — uncommitted changes, or commits the session did not make. Commit at the seam you are standing on and most of the list stops being able to hurt you, because there is nothing uncommitted left for
reset --hardto discard. The block is not silent either: you get a notification, and the action is listed in/permissionsunder Recently denied, whererretries it with a manual approval. -
A session produced a refactor, the bug fix it enabled, and a drive-by typo correction in an unrelated file. Argue for the number of commits you would want, in terms of what you might need to revert next week.
Show answerAnswer
Three. Next week you may need to revert the refactor and keep the fix, or drop the fix and keep the refactor, and neither is possible while they share a commit. The typo lives in an unrelated file, which is a seam of its own — the request changed subject, and an unrelated change does not belong in the same revert. A commit is only useful later if it is a state you would want back.
The tempting move is the single sentence at the end of a long session, "commit my changes": a perfectly good instruction that produces a perfectly bad commit, honestly describing all three and independently reverting none of them. The tell is in the summary you ask for first — if it needs the word "and" twice to be accurate, that is more than one commit. (This seam rule is the course's judgement, not documented behaviour.)
Sources
- code.claude.com/docs/en/best-practices
- code.claude.com/docs/en/overview
- code.claude.com/docs/en/how-claude-code-works
- code.claude.com/docs/en/commands
- code.claude.com/docs/en/interactive-mode
- code.claude.com/docs/en/checkpointing
- code.claude.com/docs/en/common-workflows
- code.claude.com/docs/en/permission-modes