Skip to main content
A branch is an isolated, writable line of history forked from main (or from any other branch). Anything you do on a branch (e.g., adding rows, changing the schema, building an index) stays on that branch, so main keeps serving production reads exactly as before. Branches are a natural fit when you want to:
  • Experiment with a new index, schema change, or reprocessing step without affecting live queries on main.
  • Run a backfill or migration you’d like to validate before applying it to main.
  • Hand a collaborator a frozen point-in-time fork while you keep writing to main.

How branches relate to versions and tags

Every LanceDB table already tracks a linear history of versions, and you can tag a version or checkout one to read it. Branches add the missing piece: a separate, writable line of history. Where a tag is a read-only label and checkout is a read-only view, a branch forks from a point in history and then evolves on its own. Creating or checking out a branch hands you a new table handle whose reads and writes are scoped to that branch. The comparison table at the end of this page lays out when to reach for each.
Branches are supported on local and namespace-backed tables in LanceDB OSS, as well as on LanceDB Enterprise (remote) tables.

Connect to a table

The branch API is identical no matter how you connect — only the connection itself differs between OSS and Enterprise. Establish a connection (db) and open a table (see Create a table), then use the same branch calls in every example that follows.

LanceDB OSS

Point LanceDB at a local directory (or an object-storage URI) to use it as an embedded library.

LanceDB Enterprise

Enterprise Branching on LanceDB Enterprise works the same way as on OSS, but you connect to your Enterprise deployment with a db:// URI, an API key, and your region. Once you have a connection, open a table and use the same branch calls in every example that follows.

Work with branches

The lifecycle of a branch is short and predictable: fork it, write to it, reopen it whenever you need it, and delete it once you’re done. The examples below use a small quotes table with three rows on main.

Create a branch

Forking from main returns a table handle scoped to the new branch. main is the reserved default source, so create needs only a name; to fork from somewhere else, pass a branch name, a specific version, or both.

Write to a branch

Writes go through the branch handle and stay there — the main handle keeps reporting its original row count. Listing branches returns a mapping of each branch name to its metadata, including the version it was forked from.

Reopen a branch

A branch outlives the handle that created it. Reopen it later by name — either from an existing table handle or straight from the connection when you open the table. Both routes give you a writable handle tracking the branch’s latest state.

Delete a branch

Deleting a branch removes it and its branch-local history; main is untouched. Before deleting a branch, make sure you’ve retained any results you need — see Apply branch-tested changes to main below.

Apply branch-tested changes to main

A branch has its own writable history. Outside of the diff and cherry-pick APIs, which are available on LanceDB Enterprise and promote added columns only, LanceDB does not reconcile one branch’s history with another or detect conflicts between them. To carry other accepted work forward, rerun the validated operation against main or explicitly write selected results to it.
How you apply a validated change depends on the type of work:
  • Added columns (Enterprise): review the branch with diff, then promote the new columns onto main with cherry_pick. See Compare and cherry-pick a branch onto main.
  • Backfill or transformation: rerun the validated job against main.
  • Schema change: apply the same reviewed schema operation to main.
  • Index change: build the index on main using the configuration validated on the branch.
  • Selected row results: upsert those rows into main using a stable unique key.

Upsert selected branch rows into main

If the result you want to retain is a set of inserted or updated rows, use merge_insert to write them to main. Despite its name, merge_insert is a row-ingestion operation: it matches incoming rows by key and does not merge branch histories. Read the rows you want from the reviewed branch, then upsert them into main on your key column — id in this example. Here, candidate is the branch handle: This operation transfers only the inserted or updated rows in its input. It does not transfer branch history, schema changes, indexes, or branch-local deletions, and it does not determine which rows changed after the branch was created. Reapplying an identical payload is idempotent on the key, but the operation is not conflict-aware. If main has diverged, incoming branch rows can overwrite newer values with the same key. Read and upsert the whole branch only when that overwrite is intentional; otherwise, filter the branch read to the rows you intend to apply.

Compare and cherry-pick a branch onto main

Enterprise On LanceDB Enterprise, branches expose two review-and-land calls that let you inspect what a branch has changed relative to main and then promote its new columns onto main in place, without reissuing the branch’s writes.
diff and cherry_pick are available on Enterprise (remote) tables only. On local tables both calls raise NotSupported. cherry_pick currently promotes added columns (including blob columns) and leaves main’s existing columns unchanged; use the upsert or rerun patterns above for row and index changes.
cherry_pick replaces the earlier merge call on table.branches. It’s a breaking rename: the merge name reads like a three-way git merge, but this API takes one additive change on a branch and lands it on main. Update calls that referenced table.branches.merge(...) to table.branches.cherry_pick(...), and read the failure list from diff["errors"] (previously mergeBlockers). table.merge_insert and Table.merge are unrelated and unchanged.

Diff a branch

diff reads the branch and main, and returns a summary of what has changed: which columns were added, removed, or altered; which indexes were added or removed; row-count deltas; and, most importantly, a list of errors explaining why the branch cannot currently be cherry-picked, if any.
Common error codes include BaseMoved (the branch’s parent no longer matches main’s latest), RowsChanged, RowCountMismatch, ColumnRemoved, ColumnChanged, NothingToApply, NoColumnChanges, InputColumnDependency, and ParentNotMain. Newer server codes surface as Unknown so older clients keep working.

Cherry-pick a branch

cherry_pick promotes a branch’s added columns onto main. It is a review-and-land operation: the server re-evaluates the diff at request time, and either lands the promotion or rejects it with the same error codes diff would report. A failed cherry-pick is not an exception. It resolves with status="failed" and populates diff.errors so you can inspect why and decide what to do next. Set dry_run=True (Python) or dryRun: true (TypeScript) to preview the cherry-pick without landing it. The result includes a preview.promotedColumns list showing which columns the cherry-pick would (or did) promote.
Possible status values are ready (returned by dry_run when the cherry-pick would land), cherryPicked (a real cherry-pick that landed), failed (server declined; see diff.errors), notImplemented, and unknown for forward compatibility. Cherry-pick requests are not retried on failure. The response carries everything you need to decide next steps.

Build indexes on a branch

One of the most useful things a branch buys you is a safe place to build and validate an index without affecting what’s in production on the main branch. Fork a branch, create your vector (ANN) and full-text search (FTS) indexes on it, and check recall and latency. Once you have selected a configuration, build the corresponding index on main through your normal deployment workflow. Because the indexes live on the branch, queries against main never see a half-built index and are never slowed down by the branch’s build. This pattern is especially valuable on Enterprise deployments, where main is typically serving production traffic while you tune an index configuration on the side. Schema changes such as adding, altering, or dropping columns are branch-scoped in the same way, so you can stage and review a larger reshaping of a table before applying the same schema operations to main.

Branches vs. tags vs. versions

Now that you’re familiar with branches, you can see how they complement the other ways LanceDB give you to work with table history. Choose these approaches based on whether you need to label, read, or write data at a point in history:
For linear version history — creating versions, listing them, rolling back, and tagging — see the Versioning and Reproducibility guide.