DEV Community

Ken Imoto
Ken Imoto

Posted on

Why 'Just Squash Merge' No Longer Works in the Age of AI

Can you revert that Squash Merge?

Claude Code and Copilot Agent now generate dozens of files and hundreds of lines in a single PR. Add test code, and it's not unusual for a PR to exceed 1,000 lines of changes.

When humans were writing code by hand, a PR was maybe 50–300 lines. Squash Merging that into one commit was fine — you could read the diff and understand what happened.

But when AI generates 1,000+ lines and you Squash Merge it into a single commit, what happens when production breaks and you need to revert? Can you find the root cause in a 1,000-line diff?

This article examines why the merge strategy debate needs revisiting now — through the lens of commit log readability and revert safety.

TL;DR

  • AI-assisted development has inflated PR sizes by 3–10x compared to hand-written code
  • Squash Merging these large PRs destroys the commit granularity needed for effective debugging
  • Merge Commits preserve individual commits, enabling safe reverts with git revert -m 1
  • The best practice: Merge Commit as default + Conventional Commits for readable logs
  • Squash Merge still has its place — but you need clear criteria for when to use it

PR Sizes Have Exploded in the AI Era

Think about what a typical PR looked like before AI coding assistants:

Era Typical PR Size Commits per PR
Hand-written 50–300 lines 3–5
AI-assisted 300–2,000 lines 5–20

Claude Code, Cursor, and GitHub Copilot Agent don't just generate feature code — they generate massive amounts of test code too. Ask for "add authentication" and you get implementation (200 lines) + tests (500 lines) + type definitions (100 lines) all at once.

In my own experience, I set up CLAUDE.md harness configuration with TDD and automated use-case testing. In this setup, a single PR generates around 50 tests. Test code alone runs to several hundred lines. Combined with the implementation, 1,000+ lines per PR is a daily occurrence.

Squash Merging this into one commit means 1,000+ lines of changes compressed into a single commit message. If you're still using Squash Merge with the same mindset from the hand-written era, you're unknowingly producing "giant single commits" at scale.

The 3 Merge Strategies in 30 Seconds

Strategy Commit History Merge Commit Revert Ease
Merge Commit All branch commits preserved Yes git revert -m 1 per PR
Squash Merge Compressed to 1 commit No ⚠️ 1 commit but contents opaque
Rebase Merge Branch commits placed on main No ❌ PR boundaries lost
main:    ──●──●──────────────●──●──
               \            /
feature:        ●──●──●──●─┘
                │   │   │
                │   │   └─ test: add specs
                │   └───── fix: validation
                └───────── feat: add login
Enter fullscreen mode Exit fullscreen mode

The Squash Merge Trap — What Happens When You Need to Revert

Scenario: Production incident, Friday evening

Error monitoring fires right after deployment. The culprit is somewhere in PR #42 (authentication feature).

With Squash Merge:

git revert a1b2c3d  # Revert PR #42
Enter fullscreen mode Exit fullscreen mode

The revert itself works. But then what?

git show a1b2c3d
# → 500-line diff. Login UI changes, validation fixes,
#   test additions, CI config changes... all in one diff.
Enter fullscreen mode Exit fullscreen mode

Can you identify which change caused the bug in a 500-line diff?

The original branch had feat: add login, fix: validation, test: add specs as separate commits. But Squash Merge permanently destroyed that granularity.

With Merge Commit:

git revert -m 1 <merge-commit-hash>  # Revert PR #42
Enter fullscreen mode Exit fullscreen mode

After reverting, investigate:

git log --oneline main..feature/auth
# feat: add login UI
# fix: validation logic for email
# test: add auth specs
# chore: update CI config
# → "fix: validation logic for email" looks suspicious → pinpoint check
Enter fullscreen mode Exit fullscreen mode

You can narrow down the cause at the commit level. This is the decisive advantage of Merge Commits.

When Commit Granularity Is Lost, You Can't Find What Broke

The fundamental problem with Squash Merge is the loss of commit granularity.

Imagine a PR contained these changes:

feat: add login UI
fix: email validation regex
refactor: extract auth middleware
test: add E2E tests
Enter fullscreen mode Exit fullscreen mode

With a regular merge, these remain as 4 individual commits. When a bug appears, git blame on the affected line instantly tells you: "It was the fix: email validation commit."

With Squash Merge, it's compressed to:

feat: add user authentication (#42)  ← All 4 changes are inside this one commit
Enter fullscreen mode Exit fullscreen mode

git blame shows every line pointing to this single commit. UI change? Validation fix? Middleware refactor? You have to read the entire diff to find out.

For a 10-line change, that's fine. But searching for a bug inside a 500 or 1,000-line Squash Merge commit is like reading through log files with your eyes instead of using grep.

Commit granularity is a search index for your future self when hunting bugs. Squash Merge is the act of throwing that index away.

Others Have Reached the Same Conclusion

"Squash Merge just sweeps problems under the carpet"

Cape of Good Code's article "Git Squash Merge Just Sweeps Problems under the Carpet" describes Squash Merge as "choosing the lesser of two evils." Either your commits are chaotic, or you crush all information into one — both are extremes. What's really needed is the ability to split commits at appropriate granularity.

"Delete the branch, and all development details are gone"

myst729's "Why I'm against merging pull requests in squash mode or rebase mode?" demonstrates with diagrams that once you Squash Merge and delete the feature branch, the development history is completely lost. With a regular merge, commit history survives on main even after branch deletion.

"The context lost is significant"

Lloyd Atkinson's "Should You Squash Merge or Merge Commit?" takes a neutral stance analyzing both approaches, noting that while Squash advocates claim "clean history," Merge Commit advocates strongly object that "too much history and context is lost in the process."

All three articles agree: Squash Merge's "clean log" isn't free — you're paying with the loss of information.

Comparison: Logs and Reverts

Aspect Merge Commit Squash Merge
git log readability ⚠️ More commits ✅ 1 PR = 1 commit
git blame precision ✅ Per-change tracking ⚠️ Per-PR only
Revert granularity ✅ Safe PR-level revert ✅ Single commit revert
Post-revert investigation ✅ Individual commits preserved ❌ Granularity lost
Conflict resolution history ⚠️ Recorded in merge commit ❌ Resolution context lost
cherry-pick ✅ Pick individual commits ❌ All-or-nothing
bisect for bug hunting ✅ Fine-grained binary search ⚠️ PR-level only

When Squash Merge IS the Right Choice

✅ Good candidates for Squash Merge

  1. PRs full of WIP/trial-and-error commitswip, fix typo, fix again, really fix × 20
  2. External contributor PRs — Commits that don't follow your team's message conventions
  3. Tiny 1–2 file changes — Changes small enough that revert investigation isn't needed

❌ Avoid Squash Merge for

  1. PRs with multiple logical changes — "auth + UI refactor + tests" combined
  2. Production-critical changes — Anything you might need to revert
  3. Long-running feature branches — Preserve the development narrative

Decision Flowchart

Merging a PR
  │
  ├─ 3 or fewer commits?
  │   ├─ Yes → Each commit message meaningful?
  │   │         ├─ Yes → ✅ Merge Commit
  │   │         └─ No  → Squash Merge
  │   └─ No  → Lots of WIP/typo commits?
  │             ├─ Yes → Squash Merge
  │             └─ No  → Production-impacting?
  │                       ├─ Yes → ✅ Merge Commit
  │                       └─ No  → Squash Merge
Enter fullscreen mode Exit fullscreen mode

git bisect — Where Merge Strategy Matters Most

git bisect performs a binary search through commits to find the one that introduced a bug:

git bisect start
git bisect bad HEAD
git bisect good v1.2.0
# → Git automatically checks out intermediate commits for you to test
Enter fullscreen mode Exit fullscreen mode

With Merge Commit: bisect can search at the individual commit level. "This one-line fix in fix: validation broke it" — pinpointed.

With Squash Merge: bisect can only narrow it down to the PR level. You find the guilty Squash Merge commit, but you're still left with a 1,000-line diff to manually search through.

As team size grows, this difference compounds exponentially. More PRs, more Squash Merge commits, more haystacks to search through when something breaks.

Practical Setup for Teams

Recommended: Merge Commit + Conventional Commits

feat: add login UI component
fix: email validation regex for edge cases
test: add authentication E2E tests
chore: update CI config to Node 22
Enter fullscreen mode Exit fullscreen mode

Meaningful commit messages make Merge Commit logs perfectly readable.

GitHub Repository Settings

Settings → General → Pull Requests

☑ Allow merge commits       ← Make this the default
☑ Allow squash merging      ← Keep for small PRs
☐ Allow rebase merging      ← Disable (recommended)
Enter fullscreen mode Exit fullscreen mode

Rebase merge erases PR boundaries — almost no benefit in team workflows.

Conclusion — From a Former Squash Merge Advocate

Honestly, I was a Squash Merge advocate for a long time. Clean logs, easier reviews — those advantages are real, and I still acknowledge them.

But after researching these cases, and more importantly, after experiencing the reality in my own projects — where Vibe Coding and AI agents produce PRs with 50 tests and 1,000+ lines of changes daily — I have to say this:

If the way code is written has changed, your merge strategy should change too.

Applying best practices from an era when humans wrote 50 lines at a time to an era when AI generates 1,000 lines is intellectual laziness. Use Merge Commit as the default. Use Squash Merge only for small PRs.

The peace of mind of being able to type git revert -m 1 on a Friday evening is worth far more than a clean-looking log.

Top comments (0)