· Ben · craft · 6 min
Your CMS can't take a pull request
The git-based CMS vs headless CMS argument is always about where the bytes live. It should be about what a human looks at before content goes live, and a pull request is four features a database CMS would have to build.
An editor renames an H1 at 4:47pm on a Friday. It's live. There is no diff, no reviewer, no build that had to pass first, and if the old headline was better you're reconstructing it from memory or from a cached search result.
Every git-based CMS vs headless CMS comparison I've read argues about storage. Files in a repo versus rows in a database. Build-time versus request-time. Portability, lock-in, developer experience, the usual columns in the usual table. All of it is true. None of it is what actually hurts. One of those two architectures has a review surface, and the other one doesn't.
That's the whole post. Here's what it means in practice.
The comparison everyone writes is about bytes
Pull up the ranking pieces for that query. They compare where content lives, how it's queried, whether you get a GUI, whether marketing can work without a developer. One of the better ones is written by a headless vendor and resolves, predictably, to "it depends on your team." Another is old enough that it doesn't mention Keystatic at all.
Not one of them asks the question a team actually hits in month three: when someone changes a published article, what does another human look at before that change is live?
In a database-backed CMS the answer is usually a preview link and a role. Maybe a workflow state. Maybe version history with a restore button, which is a real feature and still not the same thing. Restore is what you do after production was wrong. Review is what you do so production was never wrong.
A pull request is four features you'd otherwise have to build
Strip the ceremony off a PR and it's four mechanisms that happen to ship together.
A diff. Line by line, word-level highlighting inside the line, the old text and the new text on the same screen. Not a preview of the result, the change itself.
A comment thread anchored to a line. Somebody objects to the third paragraph and the objection lives on the third paragraph, not in Slack, not in a Google Doc that diverged from the article two hours ago.
A gate. CI runs the site build, the link checker, the frontmatter schema validation, whatever else you've bolted on. If the post breaks the build, the merge button is red before a human has to notice.
A revert. One commit, one button, with a permanent record of who merged what and when.
Now cost those four out as feature requests against a hosted CMS. Line-level diffs on rich text. Threaded comments pinned to a text range. A pre-publish hook that can actually block. An atomic undo across every field of an entry. Some products have one or two of these. Nobody has all four, because building them means rebuilding version control inside a database, and version control is a solved problem that lives one directory up from your content.
Git-based CMS vs headless CMS is a question about review, not storage
Frame it that way and the trade stops being aesthetic.
A headless CMS optimizes for a content model that many surfaces read from. That's a real job and files are bad at it. If your product needs the same product description on a website, an iOS app, an email and a partner API, a database is the correct answer and I'm not going to argue you out of it.
A git-based CMS optimizes for a change that many people have to agree on before it's real. Blog posts are that. Docs are that. Changelogs, landing page copy, anything with a legal or technical claim in it, all that.
Most teams have both jobs and pretend they have one. Your product catalog belongs in a database. Your blog is a folder of MDX, and the reason it feels chaotic is that you're publishing it through a tool with no concept of a proposed change. I wrote more about what that discipline looks like day to day in review content the way you review code, but the short version is that the review surface is the product.
The git-based CMSs already conceded the point
Here's the part none of the comparison articles connect. The tools on the git side didn't build fancier editors. They built pull requests into the editing flow, independently, three times.
Decap ships publish_mode: editorial_workflow. Turn it on and every unpublished entry becomes its own branch with an open PR against it. The kanban board in the UI is a view over those branches.
TinaCMS opens a draft PR the moment an editor saves against a protected branch. The stated reason is that CI has to pass before anything reaches production, which is the gate argument, made by a CMS vendor, in their own docs.
Keystatic installs a GitHub App and puts a branch dropdown in the editor chrome. The editor picks a branch. Content is a proposal until someone merges it.
Three teams, different stacks, different funding, same conclusion: if your content is in Git, the correct review UI is the one Git already has. The question stopped being whether editors can handle branches. It became how much of the branch you hide from them.
The honest cost
Git costs you something here. Pretending otherwise is how tools get sold.
Two editors on the same file produce a merge conflict, and a merge conflict in prose is worse than a merge conflict in code, because no compiler tells you which version was right. Preview requires a deploy per branch, which means your host's build minutes are now part of your editorial workflow. Non-technical editors need the branch concept hidden well or they'll ask what "rebase" means at the worst possible moment.
And the abstraction leaks. Decap has a long-running open issue (#7200) about exactly this: the editorial workflow's internal review state and GitHub's actual PR approval are not the same thing, so approving a PR in GitHub doesn't necessarily move the card in the CMS. Two sources of truth for "is this reviewed." That's the tax you pay for a UI that wraps Git instead of being Git.
I'd still take it. A merge conflict is a problem you can see. An unreviewed publish is a problem you find out about from a customer.
Agents are what made this urgent
Until recently this was a preference. Now it isn't.
Strapi's MCP server is generally available. Sanity markets itself for agentic applications. Both of those mean the same thing operationally: a model holds a write token pointed at your production content store. It creates entries. It edits published ones. There is no branch, because there's nothing to branch.
A model with write access and no review surface is a deploy straight to prod, every time, forever.
Point the same model at a repo and its output is a branch. You read the diff, you leave a comment, CI runs, and if the whole thing is wrong you close the PR and nothing happened. Nobody has to enforce that. It's the shape of the tool.
This is the entire reason Contentcron writes to contentcron/<slug> and opens a PR instead of publishing anything. The article shows up as a diff with an explanation of what it covers and why. You comment on line 34, it comes back as a revision commit on the same branch in about a minute, with a reply summarizing what changed. Prefix a comment with !cc-ignore and it's ignored. Nothing reaches your main branch until a person clicks merge, unless you explicitly turn that off.

Which is why we don't work with Contentful, Sanity, Strapi or WordPress
There's no file to open a pull request against. That's not a roadmap item, it's the argument. We could build an integration that pushes drafts into a headless CMS and calls it a day, and the product would immediately be the thing I just spent a thousand words criticizing: content appearing in a system with no diff and no gate.
So the fit list is boring and short. Next.js, Astro, Hugo, Gatsby, Eleventy, anything that reads MDX or Markdown out of a repo. Decap, Sveltia, TinaCMS, Keystatic, Outstatic, Pages CMS, CloudCannon, Front Matter in VS Code, because all of those are already a repo wearing an editor. The repo is the integration. There's no plugin.
If your blog lives in a database, we're not your tool. I'd rather say that on the pricing page than discover it on a support thread.
If your posts are already a folder of Markdown and the thing you're missing is somebody to keep filling it, that's what Contentcron does: one researched article a day, arriving as a PR you can reject. First one's free, no card required. Read the diff before you decide.