History

Every page write is an entry in the git log over site/pages/, with Sync to a remote.

On this page

Draft, Save, History

Three words, one meaning each. A draft is the editor's autosave, _draft.json. It is gitignored, and only editors see it in the preview. Save turns the draft into the page. History is the page's git log over site/pages/, a git repository of its own: every page write is an entry in it, so editors never handle commits.

What is recorded

A page's history is its own files, not its child pages, which have histories of their own. Every write commits on its own, whoever makes it: the admin, the CLI, an import or a route of your own. A page saved, deleted or moved, and a file of a page stored, changed or deleted, is an entry. A failed commit is logged and the write still stands.

A bulk change can be one entry instead of many. $app->history()->batch('Import products', fn() => ...) records every write the callback makes under that message, and nothing if it throws. Users are accounts, not content, and a collection is never part of history.

Sync

Sync records anything still uncommitted under site/pages, such as a hand edit or a write whose commit failed, as one entry, fast-forwards from the remote, then pushes. It never merges, so content can't be left with conflicts: if the two histories diverged, it stops and says a developer has to resolve it. The History page fetches the remote's status when it opens, at most once a minute and with a timeout.

The key History uses to push and pull is kept in site/data/git/, and backup:create saves it.

Restoring

Restoring a revision checks the page's own files out of that commit and commits them as a new entry, so history is never rewritten. Its child pages keep their current state. A page URL with ~{revision} on the end, as in /blog/my-post~3f9a1c, shows the page as it was at that revision, read-only and for editors only.

Commit messages

The message is a template under the git key, set globally in config and overridable per view in site/views/{view}/_index.json. The placeholders are {objecttype}, {type} (the view name), {action} (create, update, restore, delete or move) and {path}.

"git": {
    "message": "{objecttype}/{type}:{action}({path})"
}

Requirements

History is core, not an addon, and $app->history() always returns a History. isAvailable() is true when site/pages/ sits inside a git repository, which needs the git binary. When it is false the admin hides its history controls.