diff --git a/docs/tables/branching.mdx b/docs/tables/branching.mdx index 627a193..12dec53 100644 --- a/docs/tables/branching.mdx +++ b/docs/tables/branching.mdx @@ -200,14 +200,19 @@ below. ## Apply branch-tested changes to `main` -A branch has its own writable history. LanceDB does not currently reconcile one -branch's history with another or detect conflicts between them. To carry -accepted work forward, rerun the validated operation against `main` or +A branch has its own writable history. Outside of the [diff and merge +APIs](#compare-and-merge-a-branch-into-main) — 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 `merge`. See + [Compare and merge a branch into `main`](#compare-and-merge-a-branch-into-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 @@ -249,6 +254,104 @@ 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 merge a branch into `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 `merge` are available on Enterprise (remote) tables only. On local +tables both calls raise `NotSupported`. `merge` currently promotes added +columns; use the [upsert](#upsert-selected-branch-rows-into-main) or rerun +patterns above for row and index changes. + + +### 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 merge blockers +explaining why the branch cannot currently be merged, if any. + + +```python Python icon="python" +diff = table.branches.diff("exp") + +print(diff["addedColumns"]) # columns the branch introduced +print(diff["mergeable"]) # True when there are no blockers +print(diff["mergeBlockers"]) # list of {"code", "message"} entries +``` + +```typescript TypeScript icon="square-js" +const diff = await table.branches.diff("exp"); + +console.log(diff.addedColumns); // columns the branch introduced +console.log(diff.mergeable); // true when there are no blockers +console.log(diff.mergeBlockers); // array of { code, message } entries +``` + + +Common merge blocker codes include `BaseMoved` (the branch's parent no longer +matches `main`'s latest), `RowsChanged`, `ColumnRemoved`, `ColumnChanged`, +`NoMergeableChanges`, `NoColumnChanges`, `InputColumnDependency`, and +`ParentNotMain`. Newer server codes surface as `Unknown` so older clients +keep working. + +### Merge a branch + +`merge` 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 blockers `diff` would report. A +rejected merge is not an exception — it resolves with `status="rejected"` so +you can inspect the blockers and decide what to do next. + +Set `dry_run=True` (Python) or `dryRun: true` (TypeScript) to preview the +merge without landing it. The result includes a `preview.promoted_columns` +list showing which columns the merge would (or did) promote. + + +```python Python icon="python" +# Preview first — this does not modify main. +preview = table.branches.merge("exp", dry_run=True) +print(preview["status"]) # "ready" | "rejected" | ... +print(preview["preview"]["promotedColumns"]) + +# Land the merge. +result = table.branches.merge("exp") +if result["status"] == "merged": + print("landed at main version", result["mainVersionAfter"]) +elif result["status"] == "rejected": + for blocker in result["diff"]["mergeBlockers"]: + print(blocker["code"], blocker["message"]) +``` + +```typescript TypeScript icon="square-js" +// Preview first — this does not modify main. +const preview = await table.branches.merge("exp", true); +console.log(preview.status); // "ready" | "rejected" | ... +console.log(preview.preview.promotedColumns); + +// Land the merge. +const result = await table.branches.merge("exp"); +if (result.status === "merged") { + console.log("landed at main version", result.mainVersionAfter); +} else if (result.status === "rejected") { + for (const blocker of result.diff.mergeBlockers) { + console.log(blocker.code, blocker.message); + } +} +``` + + +Possible `status` values are `ready` (returned by `dry_run` when the merge +would land), `merged` (a real merge that landed), `rejected` (server declined; +see `diff.mergeBlockers`), `notImplemented`, and `unknown` for forward +compatibility. Merge requests are not retried on rejection — 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