Local-First Content Workflows: Git as the Ultimate CMS Database
Pillar: Git as Your CMS Database
Every CMS needs somewhere to keep content: a place with history, the ability to branch and merge, and a way to undo a mistake. Most CMSs build that from scratch — a database schema, a revisions table, a soft-delete flag. Carapace doesn't build it, because git already is that, and has been for two decades.
What you get for free
A draft is a branch. Editing a post is a commit. Publishing is a merge. None of this is a metaphor layered on top of git — it's the literal mechanism. draft/blog/your-post-slug is both the lock (nothing else can start a competing draft on that slug while the branch exists) and the working copy, at the same time, because a branch already behaves like both.
Rollback follows the same pattern. Undoing a bad edit is git revert. Undoing a publish goes through a real, reviewable pull request rather than a silent overwrite — the history shows exactly what changed and when, because it's git history, not a log Carapace maintains separately and could fall out of sync with reality.
Delete isn't actually permanent, because nothing in git ever is. Every version of every file that existed is still sitting in git's own history, whether or not it's currently live. Restoring a deleted entry means reading that history back out — not recovering from a backup, hoping one exists.
Local-first, until it needs to be live
The content repository can be purely local while you're drafting. There's no requirement to push anything until you're ready. The one place that stops being true is the actual deployed site: a build container somewhere else has to be able to reach your content to publish it, so a live site needs the content repo to have some reachable remote. Local-only stays completely valid for writing and reviewing — it just isn't sufficient on its own for a page the public can see.
Two repos, not one, is a deliberate part of this too. Git's access controls apply to a whole repository or branch, not a folder inside one — there's no native way to say "only the content agent may write here, but a developer may write there" inside a single shared repo. Splitting them gets that isolation for free, using permissions that already exist, with no custom enforcement code required.
The database was never the hard part of a CMS. It was always going to be maintaining one. Git already solved that; the interesting work was just noticing it didn't need to be solved twice.