Field note / Git

Cleaning Up Your Git History After AI-Assisted Development

AI coding agents work in rapid iteration loops that produce dozens of micro-commits. Here's how to clean up your git history before merging.

6 min read

  • Git
  • AI
  • Claude Code
  • Cursor

If you’ve used AI coding tools like Claude Code, Cursor, Copilot, or any agent-driven workflow, you’ve probably noticed something: your git history gets messy. Fast.

You ask the AI to build a feature, and it gets to work. But it doesn’t write everything perfectly in one pass. It writes some code, runs the tests, sees a failure, fixes an import, runs again, tweaks a type, runs again. Each cycle often produces a commit. By the time the feature is done, your branch has 15, 20, sometimes 30+ commits that look like this:

a1b2c3d Add initial payment service
f4e5d6c Fix missing import
7g8h9i0 Update return type
b2c3d4e Fix test assertion
e5f6g7h Add error handling
1a2b3c4 Fix typo in error message
d4e5f6g Update test mock
8h9i0j1 Fix null check
k2l3m4n Add validation
5o6p7q8 Fix validation edge case
r9s0t1u Update test for validation
...

That’s not a feature story. That’s a debugging diary. And nobody wants to review that in a PR.

Why AI Tools Produce So Many Commits

To understand the mess, you need to understand how these tools work.

The Rapid Loop Pattern

Most AI coding agents don’t write your entire feature in one shot. They work in tight iteration loops:

  1. Write some code based on your prompt
  2. Run the tests or build
  3. Read the error output
  4. Fix the issue
  5. Repeat until everything passes

Each iteration is a small, focused change. Some tools commit automatically after each step. Others encourage you to save checkpoints as you go, which is smart since it gives you a rollback point if the AI goes off track.

The problem isn’t the process. The process is actually great for development. The problem is that these iteration commits end up in your branch history, and they tell the wrong story.

Checkpoints vs. History

There’s an important distinction here. During development, frequent commits are useful. If the AI makes a wrong turn on iteration 12, you can roll back to iteration 11 and try a different approach. Those checkpoint commits are safety nets.

But once the feature is done and working, those checkpoints become clutter. Your project’s git history should describe what was built and why, not the step by step process of how an AI debugged its way to a solution.

The Cleanup: git reset --soft

This is where git reset --soft becomes your best friend. If you’ve read my previous post on squashing commits, you already know the technique. Here’s how it applies specifically to AI-assisted workflows.

The Basic Cleanup

After your AI agent is done and everything is working:

# Create a backup branch just in case
git checkout -b feature-backup

# Go back to your working branch
git checkout feature-branch

# Reset to the point where your feature work started
# Replace "main" with your base branch (master, develop, etc.)
git reset --soft main

# All your changes are now staged, commit them as one
git commit -m "Add payment processing with Stripe integration"

That’s it. Every change from all those micro-commits is now in a single, clean commit. Your files haven’t changed at all. Only the history did.

Finding the Right Reset Point

The trickiest part is knowing which commit to reset to. You have a few options:

If you branched from main:

git reset --soft main

If you branched from another branch:

# Check where your branch diverged
git log --oneline main..HEAD

# Or if you know the exact commit
git reset --soft <commit-sha>

If you’re not sure where the feature work started:

# This shows all commits on your branch that aren't on main
git log --oneline main..HEAD

The last commit in that list is your first feature commit. Reset to its parent.

When You Want to Keep Some Structure

Sometimes squashing everything into one commit is too aggressive. Maybe your branch has two logical features, or you want to separate the migration from the application code.

In that case, you can do multiple soft resets with selective staging:

# Reset all commits but keep changes staged
git reset --soft main

# Unstage everything
git reset HEAD

# Now selectively stage and commit in logical groups
git add src/migrations/
git commit -m "Add payment tables migration"

git add src/payments/ tests/payments/
git commit -m "Add payment processing service with tests"

This gives you two clean, logical commits instead of one giant one or twenty tiny ones.

Practical Tips for AI-Assisted Workflows

After months of working with AI coding tools, here are some patterns that make the cleanup process smoother.

1. Branch Early, Branch Often

Before asking the AI to work on something, create a feature branch. This way your main branch stays clean and you always have a clear reset point.

git checkout -b feature/payment-processing
# Now let the AI go wild

2. Let the AI Commit Freely During Development

Don’t fight the micro-commits during development. They’re actually valuable as checkpoints. If the AI takes a wrong turn, you can git reset --hard HEAD~1 and try a different prompt.

The cleanup comes at the end, not during the process.

3. Review Before Squashing

Before you squash, take a moment to review the full diff:

git diff main..HEAD

This shows you the complete set of changes that will end up in your squashed commit. Make sure everything looks right. Sometimes AI tools leave behind debug logs, commented out code, or unnecessary imports that are easy to miss when changes are spread across 20 commits.

4. Write a Meaningful Commit Message

This is where you add the value that the AI micro-commits were missing. Instead of “fix import” and “update test,” write a commit message that explains the feature:

git commit -m "Add Stripe payment processing

Implements the payment service with support for one-time charges
and subscription billing. Includes input validation, error handling,
and webhook endpoint for payment status updates.

Closes #142"

The commit message should tell a future developer (or future you) what this change does and why it exists.

5. Keep the Backup Branch Until the PR is Merged

Don’t delete your backup branch with all the original micro-commits until your PR is merged and deployed. If something goes wrong during review and you need to understand the step by step changes, those original commits are still there.

# After your PR is merged and deployed
git branch -D feature-backup

A Complete Workflow Example

Here’s the full workflow I follow when using AI coding tools on a project:

# 1. Start a feature branch
git checkout main
git pull
git checkout -b feature/user-notifications

# 2. Work with the AI tool (commits happen naturally during iteration)
# ... 18 commits later, the feature is complete and tests pass ...

# 3. Review the total diff
git diff main..HEAD

# 4. Create a backup
git checkout -b feature/user-notifications-backup
git checkout feature/user-notifications

# 5. Squash into clean commits
git reset --soft main

git reset HEAD
git add src/migrations/
git commit -m "Add notifications table and indexes"

git add src/ tests/
git commit -m "Add user notification system

Implements email and in-app notifications with user preferences,
batching for high-frequency events, and retry logic for failed
email deliveries.

Closes #98"

# 6. Push and open a PR
git push -u origin feature/user-notifications

Clean, reviewable, and the git history tells a story that makes sense.

Wrapping Up

AI coding tools are incredibly productive, but they trade clean git history for speed. That’s a fair trade during development. The key is knowing how to clean up after the work is done.

git reset --soft is the simplest tool for the job. Let the AI iterate freely, then squash the noise into meaningful commits before you open that PR. Your reviewers will see a clean, logical set of changes instead of a 20 commit debugging saga.

The AI writes the code. You curate the history.

← All writing