Skip to content

Repo (Push / Pull)

butterbase repo syncs a folder to your app’s repo snapshots. Pushes are content-addressed: unchanged files aren’t re-uploaded. The five most recent snapshots are retained.

Repo storage is not the same as butterbase storage (user-uploaded files). It’s a dedicated, reserved area for your app’s source code. Limits: 10 MB per file, 100 MB per snapshot.

Terminal window
butterbase repo init <app_id>

Writes .butterbase/config.json with currentApp set, and seeds a .butterbaseignore if none exists. Use --force to overwrite an existing .butterbase/config.json (the pinned_snapshot_id and other fields are preserved). Use --no-ignore to skip the .butterbaseignore seed.

Terminal window
butterbase repo push [--message "<text>"] [--dry-run] [--json]

Walks the folder, hashes each file, calls the server’s prepare endpoint, uploads any blobs the server doesn’t already have, and commits. On success, updates .butterbase/config.json with the new pinned_snapshot_id.

--dry-run prints the manifest summary (sha + size + path per file) without contacting the API. Useful for inspecting what would be pushed.

Terminal window
butterbase repo pull [--force] [--json]

Three-way reconcile against the pinned snapshot:

  • Files in the remote latest that don’t match local → fetched and overwritten.
  • Files locally that aren’t in latest:
    • If their content matches the pinned snapshot (you haven’t touched them), deleted silently.
    • Otherwise flagged as a conflict; exits with code 1 unless --force is passed.

If the pinned snapshot has been pruned server-side, untracked-local deletions always require --force.

Terminal window
butterbase repo status [--json]

git status-style summary:

  • M modified locally vs the pinned snapshot
  • ? untracked (not in pinned)
  • D deleted (in pinned, missing locally)
  • N new on remote (in latest but not in pinned and not locally — pull would bring it in)
Terminal window
butterbase repo log [--json] # snapshot history newest-first
butterbase repo wipe # destructive — name-confirm prompt

Three layers, highest-precedence wins:

  1. .butterbaseignore (negates a default with !path)
  2. .gitignore
  3. Hardcoded defaults: .git/, node_modules/, dist/, .next/, .turbo/, .DS_Store, .butterbase/
Terminal window
butterbase visibility <public|private> [--listed|--unlisted] [--app <id>]

Marks the app public or private. When public, --unlisted hides it from the templates browse listing (/v1/templates, the dashboard Templates page, and butterbase templates) while still allowing clone by direct app id.

Terminal window
butterbase clone <source_app_id> [target_dir] [--name "<text>"] [--region <region>] [--json]

Creates a fresh, owned app cloned from a public template. The CLI:

  1. Calls the clone endpoint to start a job.
  2. Polls until the job completes (max 5 minutes).
  3. butterbase repo init <new_app_id> + butterbase repo pull in <target_dir> (defaults to a dir named after the new app id).

Clones receive the source’s schema, RLS policies, function code, non-secret config, repo files, and rows from _seed: true tables. End-user accounts, secrets, env vars, BYOK AI keys, custom domains, and audit logs stay with the source. See What a clone copies for the full breakdown.

The butterbase clone command prints any warnings returned by the clone job — for example, if the source’s auth_hook_function references a function that didn’t replay onto the destination, the binding is left NULL and a warning is printed.

If the clone fails, the error is printed and exit code is 1. Failed jobs can be retried via the HTTP API directly (POST /v1/clone-jobs/<job_id>/retry).