> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Push & Pull

> Sync XanoScript files between your local filesystem and Xano

export const BrowserFrame = props => {
  const {url = "xano.run", maxWidth = 820, className = "", lightSrc, darkSrc, alt = "", children} = props || ({});
  const style = typeof maxWidth === "number" ? {
    maxWidth: `${maxWidth}px`,
    margin: "16px 0"
  } : {
    maxWidth,
    margin: "16px 0"
  };
  const hasSwapImages = Boolean(lightSrc && darkSrc);
  return <div className={`browser-frame ${className}`.trim()} style={style}>
      <div className="browser-frame__top">
        <div className="browser-frame__controls" aria-hidden="true">
          <span className="browser-frame__dot browser-frame__dot--red" />
          <span className="browser-frame__dot browser-frame__dot--yellow" />
          <span className="browser-frame__dot browser-frame__dot--green" />
        </div>
        <div className="browser-frame__address">{url}</div>
      </div>

      <div className="browser-frame__body">
        {hasSwapImages ? <>
            <img className="browser-frame__img--light" src={lightSrc} alt={alt} />
            <img className="browser-frame__img--dark" src={darkSrc} alt={alt} />
          </> : children}
      </div>
    </div>;
};

The Xano CLI lets you pull your entire workspace down as XanoScript files and push local changes back. This enables local development, version control with Git, AI-assisted development, and team collaboration.

<Info>
  **Switching back to the CLI after working in another build mode?** If you've made changes using the visual builder, the in-browser XanoScript editor, the VS Code extension, or the MCP server, your local CLI files won't reflect those changes automatically — there is no automatic sync or diff checking. Always run `xano workspace pull` to download the latest state of your workspace before making further changes locally.
</Info>

<Tip>
  **On a paid plan?** Pushing through your [sandbox](/xano-cli/sandbox) is the default — and is required unless you've enabled **Allow Direct Workspace Push** in **Xano → Workspace Settings**. `xano sandbox push` shares most of `xano workspace push`'s flags, with one exception: it does **not** support the `-i/--include` or `-e/--exclude` filters. See the [Typical Workflow](#typical-workflow) below.
</Tip>

## Pull

Pull downloads your workspace as individual `.xs` (XanoScript) files, organized by type. Behind the scenes, the CLI retrieves a single [multidoc](/xanoscript/multidoc) — a combined XanoScript document containing every definition in your workspace — and splits it into separate files.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace
  ```
</BrowserFrame>

This creates a directory structure like:

```
my-workspace/
├── workspace/
│   ├── my_workspace.xs
│   └── trigger/
│       └── on_workspace_event.xs
├── table/
│   ├── user.xs
│   ├── product.xs
│   └── trigger/
│       └── on_user_create.xs
├── function/
│   ├── calculate_shipping.xs
│   └── utils/
│       └── validate_email.xs
├── api/
│   ├── user/
│   │   ├── api_group.xs
│   │   ├── get_user_get.xs
│   │   └── create_user_post.xs
│   └── product/
│       ├── api_group.xs
│       └── list_products_get.xs
├── task/
│   └── cleanup_expired_sessions.xs
├── ai/
│   ├── agent/
│   │   ├── support_bot.xs
│   │   └── trigger/
│   │       └── on_agent_event.xs
│   ├── tool/
│   │   └── search_knowledge_base.xs
│   └── mcp_server/
│       ├── my_mcp_server.xs
│       └── trigger/
│           └── on_mcp_event.xs
├── realtime/
│   ├── channel/
│   │   └── notifications.xs
│   └── trigger/
│       └── on_message.xs
├── middleware/
│   └── auth_check.xs
└── addon/
    └── fetch_related.xs
```

Each resource becomes its own `.xs` file. Key details:

* **API endpoints** are grouped under `api/{group_name}/` directories, with each group containing an `api_group.xs` and endpoint files named `{name}_{verb}.xs`
* **Hierarchical functions** (with `/` in the name) are split into subdirectories
* **AI resources** (agents, tools, MCP servers) are grouped under `ai/`, with triggers in `ai/{type}/trigger/`
* **Realtime** resources are organized under `realtime/channel/` and `realtime/trigger/`
* **Triggers** are placed in `trigger/` subdirectories within their parent type (`table/trigger/`, `ai/agent/trigger/`, etc.)
* All filenames are converted to `snake_case`

### Pull Options

| Flag        | Description                                              |
| ----------- | -------------------------------------------------------- |
| `-b`        | Branch name (overrides profile; defaults to live branch) |
| `-w`        | Workspace ID (overrides profile)                         |
| `--env`     | Include environment variables                            |
| `--records` | Include database records                                 |
| `--draft`   | Include draft versions of resources                      |

#### Include environment variables

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace --env
  ```
</BrowserFrame>

#### Include database records

Pull your table records alongside the schema. This is also a convenient way to create a **local backup** of your data before pushing schema changes.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace-data --records
  ```
</BrowserFrame>

#### Both

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace --env --records
  ```
</BrowserFrame>

#### Pull a specific branch

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace -b v2-feature
  ```
</BrowserFrame>

If `-b` is not provided, the pull targets the branch stored in your profile. If no branch is set in your profile, it defaults to the live branch.

***

## Push

Push uploads local `.xs` files back to your Xano workspace, syncing your local changes. By default, only changed files are pushed (partial mode). Use `--sync` to push all files.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace
  ```
</BrowserFrame>

The CLI recursively collects all `.xs` files from the directory, compares them against your workspace, and sends only the changes to Xano. Before applying anything, the CLI shows a [preview](#push-preview) of what will be created, updated, or deleted, and prompts for confirmation. After completion, the CLI displays the total execution time.

| Flag               | Description                                                                                                                          |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `-b`               | Branch name (overrides profile; defaults to live branch)                                                                             |
| `-w`               | Workspace ID (overrides profile)                                                                                                     |
| `-i, --include`    | Glob pattern to include files (e.g. `-i "function/*"` or `-i "**/book*"`). Supports multiple `-i` flags.                             |
| `-e, --exclude`    | Glob pattern to exclude files (e.g. `-e "table/*"` or `-e "**/test*"`). Supports multiple `-e` flags.                                |
| `--sync`           | Full push — send all files, not just changed ones. Required for `--delete`.                                                          |
| `--delete`         | Delete workspace objects not included in the push (requires `--sync`)                                                                |
| `--records`        | Include table records in the push — requires local files pulled with `--records`                                                     |
| `--env`            | Include environment variables in the push — requires local files pulled with `--env`                                                 |
| `--dry-run`        | Show the push preview, then exit without applying changes                                                                            |
| `--force`          | Skip the preview and confirmation prompt — also overrides critical error blocking. Required in non-interactive (CI/CD) environments. |
| `--no-guids`       | Skip writing server-assigned GUIDs back to local files                                                                               |
| `--no-transaction` | Skip wrapping the import in a database transaction — useful for debugging failed imports                                             |
| `--truncate`       | Truncate all table records before importing                                                                                          |

<Warning>
  Every push automatically shows a [preview](#push-preview) of what will change and prompts for confirmation — but that's your last line of defense, not your first.

  **Before pushing:** make sure your local files are up to date (`pull` first), review your changes with `git diff`, and consider pulling with `--records` to snapshot your data before schema changes. Work on a **non-live branch** and test before promoting to live. Use `-i` to include specific files or `-e` to exclude files if you only need to update a subset.
</Warning>

See [Push Options](#push-options) for detailed usage of each flag.

***

## How objects are identified (binding local to remote)

When you push, Xano decides whether each local `.xs` file **updates an existing object** or **creates a new one** by reading a single line inside the file: its `guid`.

```
guid = "0zDkg0JfwQkH9_AyPMmrsUAEqbg"
```

**Object identity is the `guid` embedded in each document. There is no separate mapping file, and file paths do not affect binding.** When you push, the CLI recursively collects every `.xs` file under the push directory, and matches each one to a workspace object by its `guid` (together with the object's type and name) — never by folder, filename, or a numeric ID.

* Files that **have a `guid`** matching an existing object are **updated (overwritten) in place**.
* Files with **no `guid`**, or a `guid` that doesn't exist on the server, are **created** as new objects.

<Note>
  This information is provided for reference; rarely will you ever have to worry about managing GUIDs unless you are deviating into a non-standard workflow.
</Note>

For the full multidoc format and the server-side matching rules, see [Multidoc → Importing and overwrite behavior](/xanoscript/multidoc#importing-and-overwrite-behavior).

### Push modes at a glance

The same `push` command behaves differently depending on the flags you combine. All modes are gated behind the [preview and confirmation](#push-preview) unless you add `--force`.

| Mode                  | Command                               | What it sends                                   | Objects in Xano **not** in your local files                   | Destructive?                                                                                                   |
| --------------------- | ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Partial** (default) | `xano workspace push`                 | Only **changed** files (diffed via the preview) | Left untouched (listed under *Remote Only* for awareness)     | No                                                                                                             |
| **Full sync**         | `xano workspace push --sync`          | **All** files, including unchanged              | Left untouched                                                | No                                                                                                             |
| **Mirror**            | `xano workspace push --sync --delete` | All files                                       | **Deleted** so the workspace exactly matches your local files | **Yes** — permanent removal. `--delete` requires `--sync`.                                                     |
| **Preview only**      | `xano workspace push --dry-run`       | Nothing (preview only)                          | Nothing                                                       | No — always safe. Note the **double dash**: it's `--dry-run`, never `-dry-run`.                                |
| **Skip confirmation** | `xano workspace push --force`         | Same as the mode it's combined with             | Same as that mode                                             | Inherits — skips the sandbox, confirmation prompt and critical-error blocking. Required in non-interactive/CI. |

Regardless of mode, matching is always by `guid` — so switching between partial and `--sync` changes *which* files are sent, never *which objects* they bind to.

### Recipe: bind local files to existing objects (and avoid duplicates)

If you're unsure whether a push will duplicate objects, this three-step loop guarantees it won't:

1. **Pull to bind.** `xano workspace pull -d ./my-workspace` — pull regenerates every `.xs` file from the server, so each one arrives carrying its object's correct `guid`. Because pull rewrites each file in full, commit or stash any local changes first so they aren't overwritten.
2. **Preview.** `xano workspace push -d ./my-workspace --dry-run` — read the labels: existing objects should show as **UPDATE** (yellow), and only genuinely new objects as **CREATE** (green).
3. **Confirm.** If the preview matches your intent, run `xano workspace push -d ./my-workspace` and confirm at the prompt.
4. **Do not create manually.** Never create an object from scratch outside of your Xano workspace's local directory.

If something you know exists shows up as `CREATE`, its file is missing the right `guid` — re-run step 1 to restore it. You never edit an identity file by hand; there isn't one.

### Common misconceptions

<AccordionGroup>
  <Accordion title="Is there a .xano/objects.json (or a manifest) that maps my objects?">
    **No.** The CLI has no object manifest, registry, or mapping file of any kind. Identity is the `guid` line stored **inside** each `.xs` file. The only local file the CLI writes is your auth profile at `~/.xano/credentials.yaml`, which is unrelated to object identity. There is nothing to hand-edit and nothing that can "lose" your objects.
  </Accordion>

  <Accordion title="Does the file path or a numeric object ID decide what gets updated?">
    **No.** Binding is by the embedded `guid` (plus type and name). Folder and filename are irrelevant — `function/foo.xs` and `functions/foo.xs` bind to the same object if they share a `guid`. A `functions/` vs `function/` prefix mismatch cannot break binding or create duplicates.
  </Accordion>

  <Accordion title="Is the flag -dry-run or --dry-run?">
    It's `--dry-run` (**two dashes**). It shows the full preview and exits without changing anything.
  </Accordion>

  <Accordion title="Does the dry-run preview tell me update vs. create?">
    **Yes.** Every object is labeled **CREATE** (green), **UPDATE** (yellow), or **DELETE** (red), per type, before the confirmation prompt.
  </Accordion>

  <Accordion title="Do I need to stop working to avoid duplicating my functions?">
    No. As long as each file carries the right `guid` — which a fresh `pull` guarantees — a push updates existing objects in place. Run `pull`, then `push --dry-run`, and confirm the objects show as UPDATE before applying.
  </Accordion>
</AccordionGroup>

***

## Push Preview

Every `xano workspace push` automatically runs a preview before making any changes. The CLI performs a dry-run against your Xano instance and shows you exactly what will happen — then prompts for confirmation before proceeding.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace
  ```
</BrowserFrame>

If there are no changes to apply, the CLI exits with `No changes to push.` If changes are detected, the preview is shown before the confirmation prompt. When records are included (`--records`), the preview also shows record counts per table.

### Summary & Changes

The preview opens with a per-type count of what will be created, updated, or deleted — color-coded green, yellow, and red — followed by the non-destructive operations: new objects being created (`CREATE`), existing objects being updated (`UPDATE`), and fields being added or modified (`ADD_FIELD`, `UPDATE_FIELD`).

This is how you confirm that a push will *update* your existing objects rather than duplicate them: an object matched by its [`guid`](#how-objects-are-identified-binding-local-to-remote) shows as `UPDATE`, while only genuinely new objects show as `CREATE`. Review these labels before confirming.

<img src="https://mintcdn.com/xano-997cb9ee/kSICa8T_Xd1c9B9B/images/CLI-push-preview-changes.png?fit=max&auto=format&n=kSICa8T_Xd1c9B9B&q=85&s=ef502d2d5f1fd1bc720d4a380678ea1c" alt="CLI push preview — changes" width="1311" height="1188" data-path="images/CLI-push-preview-changes.png" />

### Destructive Operations

Operations that can cause data loss are listed separately and highlighted: `DELETE`, `CASCADE_DELETE`, `TRUNCATE`, `DROP_FIELD`, and `ALTER_FIELD`. When destructive operations are present, the confirmation prompt explicitly warns you before asking to proceed.

<img src="https://mintcdn.com/xano-997cb9ee/kSICa8T_Xd1c9B9B/images/CLI-push-preview-destructive.png?fit=max&auto=format&n=kSICa8T_Xd1c9B9B&q=85&s=2b1a64d48484d142021d908d2d2dcc1d" alt="CLI push preview — destructive operations" width="1302" height="474" data-path="images/CLI-push-preview-destructive.png" />

<img src="https://mintcdn.com/xano-997cb9ee/kSICa8T_Xd1c9B9B/images/CLI-push-preview-destructive2.png?fit=max&auto=format&n=kSICa8T_Xd1c9B9B&q=85&s=4d8f3be05b29f9057a5c0052d5e69ba5" alt="CLI push preview — destructive operations 2" width="1310" height="668" data-path="images/CLI-push-preview-destructive2.png" />

### Remote Only

Objects that exist in your Xano workspace but are not in your local files. These are shown when `--delete` is not set — they will not be deleted, but the preview makes them visible so you're aware of the discrepancy.

<img src="https://mintcdn.com/xano-997cb9ee/kSICa8T_Xd1c9B9B/images/CLI-push-preview-remote.png?fit=max&auto=format&n=kSICa8T_Xd1c9B9B&q=85&s=b2943dcb56c07b6265b5cb191eb56e94" alt="CLI push preview — remote only" width="1298" height="557" data-path="images/CLI-push-preview-remote.png" />

### Verbose mode

Add `-v` to see a `reason` line under each operation explaining why the server classified it that way:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace -v
  ```
</BrowserFrame>

### Preview only (dry run)

Use `--dry-run` to see the full preview — changes, destructive operations, remote-only items, and records — then exit without pushing. Nothing is applied.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --dry-run
  ```
</BrowserFrame>

A `--dry-run` prints exactly what a real push would show, then stops. Here's a sample, colored as it appears in your terminal:

<div style={{background: '#0d1117', color: '#c9d1d9', padding: '1rem 1.25rem', borderRadius: '8px', overflowX: 'auto', fontSize: '0.8125rem', lineHeight: 1.6, margin: '1rem 0', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', whiteSpace: 'pre'}}>
  <div><span style={{color: '#e6edf3', fontWeight: 600}}>=== Push Preview: workspace 5 ===</span></div>
  <div><span style={{color: '#7d8590'}}>  instance: xuwv-vqfi-rpkp.xano.io  |  workspace: Integration Builder Dashboard  |  cli: v1.0.4</span></div>

  <div>
    {' '}
  </div>

  <div>  Functions            <span style={{color: '#3fb950'}}>+1 created</span>  <span style={{color: '#d29922'}}>\~2 updated</span></div>
  <div>  Tables               <span style={{color: '#d29922'}}>\~1 updated</span></div>

  <div>
    {' '}
  </div>

  <div><span style={{color: '#e6edf3', fontWeight: 600}}>--- Changes ---</span></div>

  <div>
    {' '}
  </div>

  <div>  <span style={{color: '#3fb950'}}>CREATE          </span> function           cf\_stripe\_finalization\_bridge</div>
  <div>  <span style={{color: '#d29922'}}>UPDATE          </span> function           cf\_stripe\_finalization\_validate</div>
  <div>  <span style={{color: '#d29922'}}>UPDATE          </span> function           cf\_stripe\_post\_order\_payment\_collection</div>
  <div>  <span style={{color: '#d29922'}}>UPDATE          </span> table              payment\_ledger</div>
</div>

Read the left column: every object matched to an existing one by its [`guid`](#how-objects-are-identified-binding-local-to-remote) shows as <span style={{color: '#d29922', fontWeight: 600}}>UPDATE</span> (yellow), and only genuinely new objects show as <span style={{color: '#3fb950', fontWeight: 600}}>CREATE</span> (green). Because this is `--dry-run`, nothing was written — it's the safe way to confirm a push will update in place rather than duplicate. When destructive operations are present (`DELETE`, `TRUNCATE`, `DROP_FIELD`), they appear in <span style={{color: '#f85149', fontWeight: 600}}>red</span> under a separate **Destructive Operations** heading.

This is useful when you want to inspect what a push *would* do before committing to it — especially helpful for AI agents that need to review the impact of changes before deciding to proceed.

### Critical error blocking

Pushes that contain critical errors — such as XanoScript syntax errors or unresolved placeholders — are automatically blocked. The preview highlights these errors so you can fix them before retrying. Use `--force` to override and push anyway.

### Skipping preview

Use `--force` to bypass the preview and confirmation prompt entirely — useful in CI/CD pipelines or scripted workflows. `--force` also overrides critical error blocking.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --force
  ```
</BrowserFrame>

<Note>
  In non-interactive environments (e.g. piped output, CI/CD), the CLI requires `--force` to skip the confirmation prompt.
</Note>

***

## Push Options

#### Push with records

Records are **off by default** on push, matching pull behavior. To include records, you must have first pulled with `--records` (so the data exists locally), then explicitly pass `--records` on push:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace-data --records
  # ... make changes ...
  xano workspace push -d ./my-workspace-data --records
  ```
</BrowserFrame>

#### Push with environment variables

Environment variables are also **off by default** on push. Pull with `--env` first, then push with `--env`:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace pull -d ./my-workspace --env
  # ... make changes ...
  xano workspace push -d ./my-workspace --env
  ```
</BrowserFrame>

#### Push with records and environment variables

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace-data --records --env
  ```
</BrowserFrame>

#### File filtering

Use `-i` (or `--include`) to push only files matching a glob pattern, and `-e` (or `--exclude`) to skip files matching a pattern. Patterns are matched against relative paths from the push directory. You can pass multiple flags to combine patterns.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  # Push only functions
  xano workspace push -d ./my-workspace -i "function/*"

  # Push anything with "book" in the name
  xano workspace push -d ./my-workspace -i "**/book*"

  # Combine include patterns
  xano workspace push -d ./my-workspace -i "function/*" -i "api/*"

  # Push functions but exclude test files
  xano workspace push -d ./my-workspace -i "function/*" -e "**/test*"

  # Push everything except tables
  xano workspace push -d ./my-workspace -e "table/*"
  ```
</BrowserFrame>

#### Full sync

By default, only changed files are pushed. Use `--sync` to push all files — this performs a full sync against your workspace.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --sync
  ```
</BrowserFrame>

#### Delete removed objects

Use `--sync --delete` to remove any objects in Xano that are not present in your local files. This ensures your workspace matches your local directory exactly. `--delete` requires `--sync`.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --sync --delete
  ```
</BrowserFrame>

<Warning>
  `--sync --delete` will permanently remove objects from your Xano workspace that don't exist in the local push. Double-check your git diff to be confident before using this flag.
</Warning>

#### Skip GUID sync

By default, after a push the CLI writes server-assigned GUIDs back into your local `.xs` files. Use `--no-guids` to skip this step — useful in CI/CD pipelines or when you don't want local files modified.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --no-guids
  ```
</BrowserFrame>

#### Skip transaction

By default, pushes are wrapped in a database transaction so that failures roll back cleanly. Use `--no-transaction` to disable this — useful for debugging failed imports where you want to inspect the partial state.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace --no-transaction
  ```
</BrowserFrame>

#### Truncate table records before importing (staging/test workspaces only)

Use `--truncate` to wipe all existing rows from every table before importing records from your local files. This is useful when you want a clean slate — for example, resetting a staging or test workspace to a known dataset before running automated tests, or re-seeding a fresh environment with fixture data.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace-data --records --truncate
  ```
</BrowserFrame>

<Warning>
  `--truncate` deletes all existing table records before importing. It is intended for **staging and test workspaces only** — never use it against a production workspace. The push preview always lists truncation under Destructive Operations so you can verify it's expected before confirming.
</Warning>

***

## Pull from Git

You can pull XanoScript files directly from a Git repository (GitHub, GitLab, or any git URL) into a local directory — without needing to clone the repo yourself.

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace git pull -d ./output -r https://github.com/owner/repo
  ```
</BrowserFrame>

This fetches all `.xs` files from the repository and organizes them into the same directory structure as a workspace pull.

### Git Pull Options

| Flag     | Description                                                                    |
| -------- | ------------------------------------------------------------------------------ |
| `-r`     | Git repository URL (required) — supports HTTPS, SSH, GitHub, and GitLab URLs   |
| `-b`     | Branch, tag, or ref to fetch (defaults to the repository's default branch)     |
| `-t`     | Personal access token for private repos (falls back to `GITHUB_TOKEN` env var) |
| `--path` | Subdirectory within the repo to import from                                    |

### URL Formats

You can pass a variety of URL formats — the CLI extracts the owner, repo, branch, and path automatically:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  # Repository root
  xano workspace git pull -d ./output -r https://github.com/owner/repo

  # Specific branch
  xano workspace git pull -d ./output -r https://github.com/owner/repo -b main

  # Subdirectory via URL path
  xano workspace git pull -d ./output -r https://github.com/owner/repo/tree/main/path/to/dir

  # Single file URL (imports the containing directory)
  xano workspace git pull -d ./output -r https://github.com/owner/repo/blob/main/file.xs

  # SSH URL
  xano workspace git pull -d ./output -r git@github.com:owner/repo.git

  # GitLab
  xano workspace git pull -d ./output -r https://gitlab.com/owner/repo/-/tree/master/path

  # Private repo with token
  xano workspace git pull -d ./output -r https://github.com/owner/private-repo -t ghp_xxx
  ```
</BrowserFrame>

<Tip>
  For GitHub repos, the CLI uses the tarball API for fast downloads without requiring `git` to be installed. For GitLab and other hosts, it falls back to a shallow `git clone`.
</Tip>

### Workflow: Import from Git, Push to Xano

A common use case is pulling XanoScript from a shared Git repository and pushing it to a workspace. For example, you can pull the **Hello World** sample from the [XanoScript examples repo](https://github.com/xano-inc/xanoscript-examples) and push it to your workspace:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace git pull -d ./helloworld \
    -r https://github.com/xano-inc/xanoscript-examples \
    --path helloworld
  xano workspace push -d ./helloworld
  ```
</BrowserFrame>

***

## Typical Workflow

On paid plans, the default development cycle pushes through your [sandbox](/xano-cli/sandbox) — an isolated copy of your workspace where you can test changes before promoting them. Direct workspace pushes are blocked unless you turn on **Allow Direct Workspace Push** in **Xano → Workspace Settings**.

<Steps>
  <Step title="Pull the latest">
    <BrowserFrame url="Terminal">
      ```bash theme={null}
      xano workspace pull -d ./my-workspace
      ```
    </BrowserFrame>
  </Step>

  <Step title="Make changes locally">
    Edit `.xs` files in VS Code with the [XanoScript Language Server](https://marketplace.visualstudio.com/items?itemName=xano.xanoscript-language-server) extension for syntax highlighting and inline validation.

    <Tip>
      Use [AI-assisted development](/getting-started-ai) with Claude Code and the Xano Developer MCP to generate and modify XanoScript with AI — the MCP provides documentation and real-time validation directly to your AI assistant.
    </Tip>
  </Step>

  <Step title="Review changes with Git">
    Before pushing, review exactly what changed using Git:

    <BrowserFrame url="Terminal">
      ```bash theme={null}
      git diff
      ```
    </BrowserFrame>

    Pay close attention to any table schema changes — renamed columns, removed fields, or changed field types — as these can affect existing data.
  </Step>

  <Step title="Push to your sandbox">
    Push your changes to your sandbox. The CLI shows the same [preview](#push-preview) and confirmation as a workspace push.

    <BrowserFrame url="Terminal">
      ```bash theme={null}
      xano sandbox push -d ./my-workspace
      ```
    </BrowserFrame>
  </Step>

  <Step title="Test in the sandbox">
    Verify your changes work as expected in the isolated sandbox environment before they touch your workspace.
  </Step>

  <Step title="Review & promote">
    Open the sandbox in the browser to review the diff against your workspace and promote the changes:

    <BrowserFrame url="Terminal">
      ```bash theme={null}
      xano sandbox review
      ```
    </BrowserFrame>

    See [Sandbox](/xano-cli/sandbox) for the full set of sandbox commands — including reset, env vars, and running tests.
  </Step>
</Steps>

<Note>
  **Pushing directly to your workspace.** Free plans always push directly. On paid plans, enable **Allow Direct Workspace Push** in **Xano → Workspace Settings** to bypass the sandbox. Then substitute `xano workspace push -d ./my-workspace --dry-run` to preview, and `xano workspace push -d ./my-workspace` to apply.
</Note>

***

## <Icon icon="https://mintcdn.com/xano-997cb9ee/aZQYcxhIvSDTNEim/images/icons/GitHub_light.svg?fit=max&auto=format&n=aZQYcxhIvSDTNEim&q=85&s=43b6a62193d3b7677b1507f3851c13c6" size={24} width="1024" height="1024" data-path="images/icons/GitHub_light.svg" /><Icon icon="https://mintcdn.com/xano-997cb9ee/aZQYcxhIvSDTNEim/images/icons/GitHub_dark.svg?fit=max&auto=format&n=aZQYcxhIvSDTNEim&q=85&s=744195baf98992d8c2e009ffafe0b937" size={24} width="1024" height="1024" data-path="images/icons/GitHub_dark.svg" /> <Icon icon="https://mintcdn.com/xano-997cb9ee/aZQYcxhIvSDTNEim/images/icons/gitlab.svg?fit=max&auto=format&n=aZQYcxhIvSDTNEim&q=85&s=8ee5edf2535d88a8b345481d673c1b9e" size={24} width="32" height="32" data-path="images/icons/gitlab.svg" /> Git works like it always has

Your workspace is plain files on disk — `git init`, commit, branch, and open PRs exactly like any other project. Xano's push/pull fits into your Git workflow, not the other way around.

## Working with Git

Since pull outputs standard files to your filesystem, you can version control your XanoScript with Git:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  cd my-workspace
  git init
  git add .
  git commit -m "Initial pull from Xano"
  ```
</BrowserFrame>

This gives you full commit history, diffs, branching, and collaboration through Git — on top of Xano's own branching system.

***

## Tips

* **Pull before you push** to avoid overwriting changes made by teammates in the Xano dashboard.
* **Use branches** for development work. Create a branch with `xano branch create dev`, then use `-b dev` on push and pull commands to target it without affecting the live branch.
* **Snapshot your data** by pulling with `--records` before pushing schema changes. This gives you a local copy of your database records you can restore from if needed. To push those records back, pass `--records` on push as well — records are off by default in both directions.
* **Use `-i` to include** specific files (e.g. `-i "function/*"`) or `-e` to exclude files (e.g. `-e "table/*"`). Use `--sync` when you need a full push, and `--sync --delete` to remove remote objects not in your local files.
* **Combine with AI tools** like Claude Code or Cursor to generate and edit XanoScript locally, then push the results. See the [Start from Scratch](/xano-cli/guide-from-scratch) and [Work from Existing](/xano-cli/guide-from-existing) guides for full walkthroughs.

***

## Troubleshooting

If a push or pull isn't working as expected, use the `-v` (verbose) flag to see the full request and response details:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  xano workspace push -d ./my-workspace -v
  ```
</BrowserFrame>

Verbose mode shows:

* The HTTP method and full URL being called
* Request headers (with the authorization token partially masked)
* The request body (truncated to 500 characters for readability)
* The response status code and elapsed time

This works on **all** CLI commands, not just push and pull. You can also enable it globally by setting the `XANO_VERBOSE` environment variable:

<BrowserFrame url="Terminal">
  ```bash theme={null}
  export XANO_VERBOSE=true
  xano workspace pull -d ./my-workspace
  ```
</BrowserFrame>


## Related topics

- [Sandbox](/xano-cli/sandbox.md)
- [Tenants](/xano-cli/tenants.md)
- [Releases](/xano-cli/releases.md)
