· 7 min read ·

Git Discipline Is More Important With Agents, Not Less

Source: simonwillison

Simon Willison has been publishing a guide to agentic engineering patterns that covers Git specifically, and the core observation is one that gets overlooked in most writing about AI coding tools: the faster an agent produces changes, the more important your version control discipline becomes, not less.

That seems backwards to a lot of people. Git hygiene feels like overhead you tolerate when working carefully, something to relax when you are just iterating fast with an AI. That intuition is wrong in a way that will eventually cost you hours.

The blast radius problem

When a human programmer makes a bad decision, it typically affects a few dozen lines of code over the course of an hour. When a coding agent makes a bad decision, it can affect thousands of lines in minutes. The speed that makes agents useful is exactly what makes undisciplined Git usage dangerous.

Without commit checkpoints, you have no recovery surface. The agent does ten things in a row, the seventh one introduces a subtle logic error, and now you are staring at a diff of 800 lines trying to figure out what went wrong. With checkpoint commits after each discrete task, you git bisect to the bad commit in seconds.

This is not hypothetical. Anyone who has worked with Aider, Claude Code, or Cursor in agent mode for more than a few sessions has experienced a runaway agent session that produced plausible-looking but broken code across many files simultaneously.

Starting clean is not optional

The most important habit is boring: run git status before you hand control to an agent. If your working tree is dirty, commit or stash everything first.

The reason is attribution. When the agent session ends and you run git diff, you want to see only what the agent did. If you started with uncommitted changes mixed in, the diff is contaminated and you cannot cleanly understand what happened. You also cannot safely git reset back to a known state without losing your own work.

This is the “clean slate” principle: git status should be empty before you start, and you should aim for it to be empty again after you commit the agent’s output. Each agent session becomes a single discrete unit of history.

Checkpoint commits versus atomic commits

There is a distinction worth making here. Atomic commits, the traditional best practice, contain exactly one logical change and leave the codebase in a working state. They are optimized for history readability and git revert precision.

Checkpoint commits are different. They are save-game points created at the end of each agent “turn,” regardless of whether the change is polished. The commit message can be rough:

wip: agent added user authentication module
wip: agent updated database schema for sessions

You are not trying to write clean history yet. You are creating recovery points before the next risky operation. When the session is complete and everything works, you can clean up with git rebase -i or git merge --squash to produce a readable commit for the branch.

The workflow looks like this:

git checkout -b agent/add-auth
# agent implements authentication
git add -p && git commit -m "wip: agent added auth module"
# agent adds session handling
git add -p && git commit -m "wip: agent added session store"
# tests pass, clean up history
git checkout main
git merge --squash agent/add-auth
git commit -m "feat: add user authentication with session handling"
git branch -d agent/add-auth

The checkpoint commits on the feature branch give you recovery granularity during the session. The squash merge gives main a clean, reviewable history.

The git add -p habit

Never run git add . after an agent session. Use git add -p instead, which stages changes one hunk at a time and shows you each one before you commit it.

This sounds tedious, but it is the primary mechanism for actually reviewing what the agent did. Files often look fine at a high level while containing subtle errors at the line level. Patch mode forces you to read the output at the granularity of the actual change.

Before staging anything, a quick sanity check:

git diff --stat

This shows which files changed and the rough scale. If you asked the agent to fix a one-line bug and --stat shows 23 files changed, something unexpected happened and you should investigate before staging anything.

Git worktrees for parallel experiments

Git worktrees allow you to check out multiple branches simultaneously in separate directories, all sharing one .git directory. This is cheap in terms of disk space and takes seconds to set up.

The practical value for agent workflows is parallelism without interference. If you want to try two different approaches to the same problem, you can run agents in two separate worktrees on two branches simultaneously:

git worktree add ../project-approach-a feature/approach-a
git worktree add ../project-approach-b feature/approach-b

# Run agents in both simultaneously
cd ../project-approach-a && claude-code "implement caching using Redis"
cd ../project-approach-b && claude-code "implement caching using in-memory LRU"

# Compare results
git diff feature/approach-a..feature/approach-b

# Keep the better one, discard the other
git worktree remove ../project-approach-b
git branch -d feature/approach-b

This is qualitatively different from running agents sequentially and comparing. Parallelism means both experiments use the same starting state, so the comparison is meaningful. Sequential experiments compound on each other’s changes.

Worktrees also help with the isolation problem. If you are running a long agent session and need to context-switch to fix a production bug, you do not have to stash or commit incomplete agent work. You have a separate worktree on main where you can do the hotfix cleanly.

Failure modes to know

Agents interact with Git in ways that can destroy work if you are not watching. The dangerous commands to be aware of:

git reset --hard HEAD~N: Agents sometimes reach for this when they think they need to “start over” on a sub-problem. This silently discards all uncommitted changes. Claude Code will ask for confirmation before running this by default, but not all tools do.

git clean -fd: Removes untracked files. An agent trying to “clean up” generated artifacts may inadvertently delete something you care about.

git commit --amend: If the previous commit has already been pushed, amending it requires a force push to sync with the remote. Agents do not always check whether a commit has been published before amending.

Force push: git push --force is the most dangerous operation in this list. It can overwrite collaborators’ work on shared branches. Aider, Claude Code, and most serious tools will either refuse or warn loudly, but you should verify this for any tool you use.

One subtle failure mode worth mentioning: merge conflict markers. Agents sometimes generate code containing literal <<<<<<<, =======, and >>>>>>> strings in ways that are not actually conflict markers, but Git’s merge machinery cannot tell the difference. When you later run git merge or git rebase, it fails with confusing conflict errors pointing at code the agent wrote.

Pre-commit hooks as automated guards

Since agents produce code that may not have been manually reviewed before committing, pre-commit hooks serve as an automated second pass. The pre-commit framework can enforce linting, type checking, secret detection, and large file rejection on every commit.

A minimal configuration for a TypeScript project:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: detect-private-key
      - id: check-added-large-files
        args: ['--maxkb=500']
  - repo: https://github.com/pre-commit/mirrors-eslint
    rev: v8.56.0
    hooks:
      - id: eslint

Secret detection is especially important. Agents will commit anything that is not in .gitignore, and they do not distinguish between a .env.example with dummy values and a .env with real credentials. Ensuring .gitignore is comprehensive before an agent session, and adding secret detection to your pre-commit hooks, provides defense in depth.

Watch for agents using --no-verify to skip hooks. This is a legitimate escape hatch for specific situations, but if an agent is routinely bypassing your hooks, the hooks are enforcing something the agent cannot satisfy, which is information you need.

Using Git history as agent context

This direction is less commonly discussed. You can feed Git history to an agent to improve the quality of its output.

git log -p -- path/to/file shows how a file has evolved over time, which tells an agent about design intent in a way that the current state of the file cannot. When you ask an agent to modify a component that has gone through several iterations, the history explains what was tried and rejected.

git blame tells an agent which parts of a file are long-established versus recently changed. Recent changes are more likely to be relevant context for whatever task you are working on.

Aider does this automatically as part of its repo-map feature, which builds context from the repository structure and recent commit history. Claude Code can be directed to do it explicitly: asking it to run git log --oneline -20 before starting work gives it a map of recent activity.

The minimum viable discipline

Not every project warrants a full ceremony around agent sessions. For a throwaway prototype, the overhead is not worth it. But there is a minimum that holds even for personal projects:

  1. Start from a clean working tree.
  2. Work on a branch, never directly on main.
  3. Commit before each operation that the agent might reverse or overwrite.
  4. Review diffs with git add -p before committing.

That four-step pattern costs maybe two minutes per session and eliminates the majority of painful recovery scenarios. Everything beyond it, worktrees, squash merges, pre-commit hooks, feeding history as context, is proportional to the stakes of the codebase and the expected duration of the agent session.

The key mental model: agents compress the time axis of development. Changes that would have taken hours happen in minutes. Git discipline does not become less relevant at higher speed; it becomes the mechanism that keeps compressed time from becoming incomprehensible.

Was this interesting?