# Theme workflow The CLI treats your storefront as a directory you can edit, diff and commit. ```bash mercemur theme pull ``` Writes your templates and builder configuration into `theme/`. ```bash mercemur theme dev ``` Prints a preview link, then uploads each save as a draft. Refresh the link to see it. Nothing reaches a shopper. ```bash mercemur theme publish ``` Promotes the draft to live. ## What lands on disk ``` theme/ ├── templates/ │ ├── home.liquid │ ├── product.liquid │ └── ... ├── online-store.json └── .mercemur-state.json ``` `templates/` holds one file per page kind. `online-store.json` is your builder document: page layout and design tokens together, written indented so its diffs are readable in a pull request. `.mercemur-state.json` records the version of each resource as of your last pull. It is **per checkout**, so do not commit it. Add it to `.gitignore`: ``` theme/.mercemur-state.json ``` If you lose it, run `mercemur theme pull` again. The CLI refuses to push without it rather than guessing, because guessing means overwriting somebody else's work. ## Draft and live are separate Every command in this workflow writes to a **draft**. Shoppers see the **published** copy. Only `publish` moves one to the other. That split is deliberate, and it is why `push` and `publish` are two commands rather than one flag. A draft save is private to you and cheap to get wrong. A publish changes what every visitor to your store sees. Collapsing them would put both behind one line in your audit log and one entry in a retry policy. ## Pushing safely Before sending anything, see where you stand: ```bash mercemur theme diff ``` That compares your files against the server's current draft, so it catches the case a dry run cannot: somebody else changed the store while you were working. ```bash mercemur theme push --dry-run ``` A dry run sends nothing and prints exactly what a real push would do, including whether each file updates something or creates it: ``` would save template home (2481 bytes, expecting 2026-08-17T10:00:00Z) would save template cart (612 bytes, new here, will be created) would save the builder config (18422 bytes, schema_version 2, expecting 2026-08-17T10:05:00Z) ``` Before sending anything, push checks every version it recorded against what the server holds now. If any of them moved, it refuses having written **nothing**: ``` changed on the server since you pulled, so nothing was written (cart, home): run `mercemur theme pull` into a clean directory and re-apply your edits, rather than pushing again ``` Retrying will not help, which is why the message says so. Pull into a clean directory, look at what changed, and re-apply. That check is not the only guard. Every individual write still carries the version you pulled, because somebody can always land a change in the gap between the check and the write. If that happens the push stops part-way and says so, rather than claiming nothing was written: ``` template cart changed on the server while this push was running, so the push stopped part-way: run `mercemur theme pull` into a clean directory and re-apply your edits, rather than pushing again ``` The fix is the same either way. The difference is only whether some templates already landed, and the message tells you which case you are in. ## Publishing `publish` promotes the draft **the server holds**, not the files on your disk. Those are usually the same thing, and when they are not it refuses: ``` template home differs from the draft on the server, so publishing would ship the older copy: run `mercemur theme push` first ``` Without that check, editing a file and forgetting to push would publish the previous version and report success. Templates publish before the builder configuration, because the configuration refers to them. The other order would briefly leave a live layout pointing at markup shoppers cannot see yet. ## Working with git The two content files are meant for version control: ```bash mercemur theme pull git add theme/templates theme/online-store.json git commit -m "storefront: new product page layout" ``` Branch, review and revert your storefront the way you do the rest of your code. To roll back, check out the older files and push them. A `git revert` changes your files, not your store. Run `mercemur theme push` and then `mercemur theme publish` to make the rollback live. ## Command reference Downloads every template and the builder configuration, and records their versions. | Flag | Default | | |---|---|---| | `--path` | `theme` | Directory to write into | | `--json` | off | Machine-readable output | Overwrites local files. Commit or stash first if you have unpushed edits. Uploads local files as a draft, then records the new versions so a second push needs no re-pull. | Flag | Default | | |---|---|---| | `--path` | `theme` | Directory to read from | | `--dry-run` | off | Print the plan, send nothing | | `--json` | off | Machine-readable output | Exits `4` if anything changed on the server since your pull. Mints a preview link and pushes each save as a draft. | Flag | Default | | |---|---|---| | `--path` | `theme` | Directory to watch | | `--debounce` | `400ms` | How long to wait for edits to settle | `--json` is refused here rather than ignored. A watch session is a stream for a person; for scripted output run `mercemur theme push --json` in your own loop. Editors write a file several times per save, so changes are debounced into one upload. A failed upload prints and the session continues; press ctrl-c to stop. The preview link lasts 48 hours and renders your **draft**. Anyone holding the link can see it, so treat it as you would a shared document. Makes the pushed draft live for shoppers. | Flag | Default | | |---|---|---| | `--path` | `theme` | Directory holding the theme | | `--json` | off | Machine-readable output | Refuses if your local files differ from the draft on the server, or if any version is unknown. Compares your local files against the draft on the server. Reads only, so it is safe to run at any time. | Flag | Default | | |---|---|---| | `--path` | `theme` | Directory holding the theme | | `--json` | off | Machine-readable output | ``` changed home new cart (push will create it) only on the server search (pull to get it; push will not remove it) ``` This answers something `push --dry-run` cannot: whether the **server** moved since your pull. A dry run only knows what is on your disk. With `--json` it reports `in_sync`, so a pipeline can check whether the storefront matches the repository. Lists past versions of the builder configuration, newest first. ``` osr_01K2R8... Mon, 17 Aug 2026 09:00:00 UTC osr_01K2P4... Sun, 16 Aug 2026 09:00:00 UTC ``` One is recorded each time the configuration is published. Templates are not listed here, because they live in your own git history; the builder document keeps server-side revisions because the dashboard edits it too. | Flag | Default | | |---|---|---| | `--show` | none | Print one revision's configuration instead of the list | | `--json` | off | Machine-readable output | Read a revision before restoring it. A list of ids and timestamps tells you *when* something changed, never *what* it changed to: ```bash mercemur theme revisions --show osr_01K2R8... ``` Copies a past revision back into the **draft**. | Flag | Default | | |---|---|---| | `--revision` | required | Id from `mercemur theme revisions` | | `--path` | `theme` | Directory holding the theme | | `--json` | off | Machine-readable output | Nothing changes for shoppers until you publish, so a restore is as reversible as any other edit until then. Afterwards run `mercemur theme pull` to bring the restored document down, then publish when you are happy with it. Like every other write, it presents the version you pulled and refuses if somebody changed the configuration in the dashboard meanwhile. ## In CI ```bash export MERCEMUR_STORE=acme.mercemur.com export MERCEMUR_TOKEN="$MERCEMUR_API_KEY" mercemur theme push --json mercemur theme publish --json ``` Check the exit code rather than parsing the message. `4` means somebody changed the store while your pipeline was running, and the right response is to fail the build and look, not to retry.