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

# Versioning

> Model document history with changes and bookmarks

Mesa records the history of every document with robust version control. Mesa's model is based on [Jujutsu VCS](https://docs.jj-vcs.dev/latest/) which allows for full Git-compatibility while enabling more efficient, agent-first workflows.

## Core Primitives

### Repository

A repository in Mesa is a folder that has its own version history and permissions.

Automatic versioning lets you view, undo, and redo modifications to documents without the fear of losing work.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const repo = await mesa.repos.create({ name: "project-1234" });
  ```

  ```python Python theme={null}
  repo = await mesa.repos.create(name="project-1234")
  ```

  ```bash CLI theme={null}
  mesa repo create project-1234 --org acme
  ```
</CodeGroup>

You'll typically want to create many repositories. For example, an app builder that turns user prompts into apps might treat every user app as a separate repository, because
two users building separate apps shouldn't affect one another's version history or documents.

### Changes

The core primitive of Mesa's version history is the `Change`. Think of a change as a logical unit of work or modification to your repository. Each change has a unique identifier
and can contain an arbitrary number of file edits. You can optionally provide a change description.

A repository for an internal dashboard might have a change history like this:

```text theme={null}
@  qzvqqupx   2026-03-16 10:42:31Z
│  Tune forecast widget behavior
○  puqltutt   2026-03-16 10:37:08Z
│  Add forecast widget
○  ovknlmro   2026-03-16 10:30:02Z
│  Seed dashboard layout
○  nuvyytnq   2026-03-16 10:24:19Z
│  Initialize dashboard repo
◆  root
```

Changes are organized in a Directed Acyclic Graph (DAG), the same way commits are in Git. This means that changes can have multiple parents and multiple children, which allows for branching and merging.

You can modify the change DAG with all the operations you know from Git: branching, merging, rebasing, reverting, cherry-picking, etc.

Create a new change on top of an existing one by passing its id as `base_change_id`. The optional `message` serves as the change description.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Buffer } from "node:buffer";

  const change = await mesa.changes.create({
    repo: "project-1234",
    base_change_id: repo.head_change_id,
    message: "Add forecast widget",
    author: { name: "Agent", email: "agent@acme.dev" },
    files: [
      {
        path: "src/widgets/forecast.ts",
        action: "upsert",
        content: Buffer.from("// forecast widget").toString("base64"),
        encoding: "base64",
      },
    ],
  });
  ```

  ```python Python theme={null}
  import base64
  from mesa_sdk import Author, FileUpsert

  change = await mesa.changes.create(
      repo="project-1234",
      base_change_id=repo.head_change_id,
      message="Add forecast widget",
      author=Author(name="Agent", email="agent@acme.dev"),
      files=[
          FileUpsert(
              path="src/widgets/forecast.py",
              content=base64.b64encode(b"# forecast widget").decode(),
          )
      ],
  )
  ```
</CodeGroup>

From inside the virtual filesystem, you can also create a new empty change on top of a bookmark and start editing files directly:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Create a new change from the tip of `main` and switch to it
  await fs.change.new({ repo: "project-1234", bookmark: "main" });

  const current = await fs.change.current({ repo: "project-1234" });

  // Or switch to an existing change without creating a new one
  await fs.change.edit({ repo: "project-1234", changeId: current.changeId });
  ```

  ```python Python theme={null}
  # Create a new change from the tip of `main` and switch to it
  await fs.changes.new("project-1234", bookmark="main")

  current = await fs.changes.current("project-1234")

  # Or switch to an existing change without creating a new one
  await fs.changes.edit("project-1234", change_id=current.change_id)
  ```

  ```bash CLI theme={null}
  # Create a new change from the tip of `main` and switch to it
  mesa new main --repo acme/project-1234

  # Or switch to an existing change without creating a new one
  mesa edit "$CHANGE_ID" --repo acme/project-1234
  ```
</CodeGroup>

<Info>
  When you mount MesaFS, your writes directly affect the
  revision you've mounted at, ex `main`.
  To keep work separate, create a new change first with `mesa new`.
  See [Writing to a Mount](/content/concepts/filesystem#writing-to-a-mount) for more details.
</Info>

### Bookmarks

Git's "branches" represent a new unit of work plus a name for that work. In Mesa's JJ-based model, units of work (Changes)
are not required to have human-readable names, however, you have the option to assign these using Bookmarks.

A Bookmark is a lightweight pointer to a specific change in the change DAG that allows you to use reference that change more easily.
Virtually every Mesa API gives you the choice to reference a change by either its ID or an associated bookmark.

Bookmarks are mutable and can be moved from one change to another.

Every repository on mesa has a default bookmark, which represents the canonical state of the project. Conventionally, the default bookmark is called `main`, although this is configurable when creating a repository.

```text theme={null}
@  qzvqqupx   2026-03-16 11:02:11Z  feature/widget
│  Polish widget interactions
│ ○  puqltutt   2026-03-16 10:58:40Z  main
├─╯  Prepare baseline release
○  ovknlmro   2026-03-16 10:30:02Z
│  Seed dashboard layout
○  nuvyytnq   2026-03-16 10:24:19Z
│  Initialize dashboard repo
◆  root
```

In the version tree above, we can see the main bookmark pointing to the `puqltutt` change and the `feature/widget` bookmark pointing to the `qzvqqupx` change.

To create a bookmark at a given change and move it onto a newer change later:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Create a bookmark pointing at a change
  await mesa.bookmarks.create({
    repo: "project-1234",
    name: "feature/widget",
    change_id: change.id,
  });

  // Move an existing bookmark to a different change
  await mesa.bookmarks.move({
    repo: "project-1234",
    bookmark: "main",
    change_id: change.id,
  });
  ```

  ```python Python theme={null}
  # Create a bookmark pointing at a change
  await mesa.bookmarks.create(
      repo="project-1234",
      name="feature/widget",
      change_id=change.id,
  )

  # Move an existing bookmark to a different change
  await mesa.bookmarks.move(
      repo="project-1234",
      bookmark="main",
      change_id=change.id,
  )
  ```

  ```bash CLI theme={null}
  # Create a bookmark pointing at a change
  mesa bookmark create feature/widget --repo acme/project-1234 --revision "$CHANGE_ID"

  # Move an existing bookmark to a different change
  mesa bookmark move main --repo acme/project-1234 --revision "$CHANGE_ID"
  ```
</CodeGroup>

To integrate the work on one bookmark into another, merge them. Mesa fast-forwards when possible and creates a merge commit when histories have diverged:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await mesa.bookmarks.merge({
    repo: "project-1234",
    target: "main",
    source: "feature/widget",
    delete_source: true,
  });

  console.log(result.merge_type); // 'merge_commit' | 'no_op'
  ```

  ```python Python theme={null}
  result = await mesa.bookmarks.merge(
      repo="project-1234",
      target="main",
      source="feature/widget",
      delete_source=True,
  )

  print(result.merge_type)  # 'merge_commit' | 'no_op'
  ```
</CodeGroup>

### Conflicts

Conflicts work exactly how they do in JJ. This is mostly the same as Git, with the sole exception that conflicts are non-blocking.
A change can be in a conflicted state and you can resolve the conflicts later or in some cases ignore them entirely. This prevents
your agents from getting stuck and gives you maximum flexibility in how to handle conflicts.

A conflict occurs when you've modified the same part of the same file in different ways on two separate branches and then try to merge them. We have multiple ways to resolve them. See [dealing with conflicts](#dealing-with-conflicts) for more details.

## Dealing with conflicts

A conflict happens when two branches start from the same base change and edit the same location differently

Example: two separate branches edit `overview.json` and change the `title` property:

**Before any merge**

Both Chat A and Chat B have edited `overview.json` in the same place.

```text theme={null}
@  qzvqqupx   2026-03-16 11:12:04Z  feature/chat-a
│  title = "Revenue + Forecast"
│ ○  puqltutt   2026-03-16 11:10:27Z  feature/chat-b
├─╯  title = "Revenue Q4 Summary"
○  ovknlmro   2026-03-16 10:58:40Z  main
│  title = "Revenue Overview"
◆  root
```

**Chat B merges first**

Chat B merges first and creates a new change on top of the main bookmark.

```text theme={null}
@  qzvqqupx   2026-03-16 11:12:04Z  feature/chat-a
│  title = "Revenue + Forecast"
○  puqltutt   2026-03-16 11:10:27Z  main
│  title = "Revenue Q4 Summary"
○  ovknlmro   2026-03-16 10:58:40Z
│  title = "Revenue Overview"
◆  root
```

Now, if we wanted to merge `feature/chat-a` into main Mesa will return an error because the new merge commit would be conflicted.

Because conflicts are non-blocking, a merge can produce a conflicted change that you resolve later. The conflicted state would look like this:

```text theme={null}
x  zxoosnnp   2026-03-16 11:13:52Z  main
│  (conflict) merge feature/chat-a into main
├─╮
│ ○  qzvqqupx   2026-03-16 11:12:04Z  feature/chat-a
│ │  title = "Revenue + Forecast"
○ │  puqltutt   2026-03-16 11:10:27Z
│ │  title = "Revenue Q4 Summary"
├─╯
○  ovknlmro   2026-03-16 10:58:40Z
│  title = "Revenue Overview"
◆  root
```

`feature/chat-a` bookmark still points to `qzvqqupx` (branched from original main change `ovknlmro`), while the conflicted merge change `zxoosnnp` combines both parents (`qzvqqupx` and `puqltutt`).

## Comparing to Git and JJ

Mesa is Jujutsu-based while retaining full Git compatibility. You can essentially treat changes like commits and bookmarks like branches but there are some key differences:

* There's no staging area like in Git. You are always editing an existing change and changes can evolve as you edit them.
  When you create a new change in Mesa it is initially empty and then you can edit and modify files at that change.
  This is ideal for agents running in sandboxes because any edits they make are automatically persisted to a very specific place in the version
  tree rather than being saved in some dangling staging area.

* Bookmarks are like branches in Git but lighter weight. They do not automatically move from one change to another but can be moved manually. The consequence of this is that you do not need to explicitly think about
  branching ahead of time. If you have a `main` bookmark, you can just create a new change from it and that's effectively an unnamed branch. You can name it later or leave it unnamed and create new changes on top of it or
  just move the main bookmark to point at the new change.

Here's a handy table to explain how Mesa version concepts map to Git and Jujutsu.

| Mesa Concept | Git      | JJ       |
| ------------ | -------- | -------- |
| Repo         | Repo     | Repo     |
| Change       | Commit   | Change   |
| Bookmark     | \~Branch | Bookmark |

### Syncing to GitHub & GitLab

Mesa can sync with arbitrary upstream git repositories including ones hosted on GitHub or GitLab. This is useful if you want to create a Mesa repository based on a template repo that lives in GitHub or use Mesa mounts as a faster way to access a GitHub-hosted repo.

Add an upstream to a Mesa repository, then trigger a sync via `syncUpstream` in the SDK or the upstream syncs REST API. See [GitHub](/content/integrations/github/sync) for setup, auth options, and sync observability.

## Next steps

* See [Filesystem](/content/concepts/filesystem)
* See [Usage Patterns](/content/usage-patterns/overview)
