|
| 1 | +--- |
| 2 | +title: "Branches" |
| 3 | +sidebarTitle: "Branches" |
| 4 | +description: "Fork isolated, writable lines of table history in LanceDB. Run experiments, backfills, and index rebuilds without disturbing production reads on main." |
| 5 | +icon: "code-branch" |
| 6 | +--- |
| 7 | +import { |
| 8 | + PyConnect, |
| 9 | + TsConnect, |
| 10 | + RsConnect, |
| 11 | + PyConnectEnterpriseQuickstart, |
| 12 | + TsConnectEnterpriseQuickstart, |
| 13 | + RsConnectEnterpriseQuickstart, |
| 14 | +} from '/snippets/connection.mdx'; |
| 15 | +import { |
| 16 | + PyBranchCreate as BranchCreate, |
| 17 | + TsBranchCreate, |
| 18 | + RsBranchCreate, |
| 19 | + PyBranchWrite as BranchWrite, |
| 20 | + TsBranchWrite, |
| 21 | + RsBranchWrite, |
| 22 | + PyBranchReopen as BranchReopen, |
| 23 | + TsBranchReopen, |
| 24 | + RsBranchReopen, |
| 25 | + PyBranchDelete as BranchDelete, |
| 26 | + TsBranchDelete, |
| 27 | + RsBranchDelete, |
| 28 | + PyBranchPromote as BranchPromote, |
| 29 | + TsBranchPromote, |
| 30 | + RsBranchPromote, |
| 31 | + PyBranchIndex as BranchIndex, |
| 32 | + TsBranchIndex, |
| 33 | + RsBranchIndex, |
| 34 | +} from '/snippets/tables.mdx'; |
| 35 | + |
| 36 | +A branch is an isolated, writable line of history forked from `main` (or from any |
| 37 | +other branch). Anything you do on a branch (e.g., adding rows, changing the schema, |
| 38 | +building an index) stays on that branch, so `main` keeps serving production |
| 39 | +reads exactly as before. Branches are a natural fit when you want to: |
| 40 | + |
| 41 | +- Experiment with a new index, schema change, or reprocessing step without |
| 42 | + affecting live queries on `main`. |
| 43 | +- Run a backfill or migration you'd like to review before promoting it. |
| 44 | +- Hand a collaborator a frozen point-in-time fork while you keep writing to `main`. |
| 45 | + |
| 46 | +## How branches relate to versions and tags |
| 47 | + |
| 48 | +Every LanceDB table already tracks a linear history of [versions](/tables/versioning), |
| 49 | +and you can [tag](/tables/versioning#tag-based-versioning) a version or `checkout` |
| 50 | +one to read it. Branches add the missing piece: a *separate, writable* line of |
| 51 | +history. Where a tag is a read-only label and `checkout` is a read-only view, a |
| 52 | +branch forks from a point in history and then evolves on its own. Creating or |
| 53 | +checking out a branch hands you a new table handle whose reads and writes are |
| 54 | +scoped to that branch. The [comparison table](#branches-vs-tags-vs-checkout) at |
| 55 | +the end of this page lays out when to reach for each. |
| 56 | + |
| 57 | +<Note> |
| 58 | +Branches are supported on local and namespace-backed tables in LanceDB OSS, as |
| 59 | +well as on LanceDB Enterprise (remote) tables. |
| 60 | +</Note> |
| 61 | + |
| 62 | +## Connect to a table |
| 63 | + |
| 64 | +The branch API is identical no matter how you connect — only the connection |
| 65 | +itself differs between OSS and Enterprise. Establish a connection (`db`) and open |
| 66 | +a `table` (see [Create a table](/tables/create)), then use the same branch calls |
| 67 | +in every example that follows. |
| 68 | + |
| 69 | +### LanceDB OSS |
| 70 | + |
| 71 | +Point LanceDB at a local directory (or an object-storage URI) to use it as an |
| 72 | +embedded library. |
| 73 | + |
| 74 | +<CodeGroup> |
| 75 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 76 | + {PyConnect} |
| 77 | + </CodeBlock> |
| 78 | + |
| 79 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 80 | + {TsConnect} |
| 81 | + </CodeBlock> |
| 82 | + |
| 83 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 84 | + { "use lancedb::connect;\n\n" } |
| 85 | + {RsConnect} |
| 86 | + </CodeBlock> |
| 87 | +</CodeGroup> |
| 88 | + |
| 89 | +### LanceDB Enterprise |
| 90 | + |
| 91 | +<Badge color="red">Enterprise</Badge> |
| 92 | + |
| 93 | +Branching on LanceDB Enterprise works the same way as on OSS, but you connect to your |
| 94 | +Enterprise deployment with a `db://` URI, an API key, and your |
| 95 | +region. Once you have a connection, open a table and use the same branch calls in |
| 96 | +every example that follows. |
| 97 | + |
| 98 | +<CodeGroup> |
| 99 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 100 | + {PyConnectEnterpriseQuickstart} |
| 101 | + </CodeBlock> |
| 102 | + |
| 103 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 104 | + {TsConnectEnterpriseQuickstart} |
| 105 | + </CodeBlock> |
| 106 | + |
| 107 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 108 | + {RsConnectEnterpriseQuickstart} |
| 109 | + </CodeBlock> |
| 110 | +</CodeGroup> |
| 111 | + |
| 112 | +## Work with branches |
| 113 | + |
| 114 | +The lifecycle of a branch is short and predictable: fork it, write to it, reopen |
| 115 | +it whenever you need it, and delete it once you're done. The examples below use a |
| 116 | +small `quotes` table with three rows on `main`. |
| 117 | + |
| 118 | +### Create a branch |
| 119 | + |
| 120 | +Forking from `main` returns a table handle scoped to the new branch. `main` is |
| 121 | +the reserved default source, so `create` needs only a name; to fork from |
| 122 | +somewhere else, pass a branch name, a specific version, or both. |
| 123 | + |
| 124 | +<CodeGroup> |
| 125 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 126 | + {BranchCreate} |
| 127 | + </CodeBlock> |
| 128 | + |
| 129 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 130 | + {TsBranchCreate} |
| 131 | + </CodeBlock> |
| 132 | + |
| 133 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 134 | + {RsBranchCreate} |
| 135 | + </CodeBlock> |
| 136 | +</CodeGroup> |
| 137 | + |
| 138 | +### Write to a branch |
| 139 | + |
| 140 | +Writes go through the branch handle and stay there — the `main` handle keeps |
| 141 | +reporting its original row count. Listing branches returns a mapping of each |
| 142 | +branch name to its metadata, including the version it was forked from. |
| 143 | + |
| 144 | +<CodeGroup> |
| 145 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 146 | + {BranchWrite} |
| 147 | + </CodeBlock> |
| 148 | + |
| 149 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 150 | + {TsBranchWrite} |
| 151 | + </CodeBlock> |
| 152 | + |
| 153 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 154 | + {RsBranchWrite} |
| 155 | + </CodeBlock> |
| 156 | +</CodeGroup> |
| 157 | + |
| 158 | +### Reopen a branch |
| 159 | + |
| 160 | +A branch outlives the handle that created it. Reopen it later by name — either |
| 161 | +from an existing table handle or straight from the connection when you open the |
| 162 | +table. Both routes give you a writable handle tracking the branch's latest state. |
| 163 | + |
| 164 | +<CodeGroup> |
| 165 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 166 | + {BranchReopen} |
| 167 | + </CodeBlock> |
| 168 | + |
| 169 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 170 | + {TsBranchReopen} |
| 171 | + </CodeBlock> |
| 172 | + |
| 173 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 174 | + {RsBranchReopen} |
| 175 | + </CodeBlock> |
| 176 | +</CodeGroup> |
| 177 | + |
| 178 | +### Delete a branch |
| 179 | + |
| 180 | +Deleting a branch removes it and its branch-local history; `main` is untouched. |
| 181 | +Delete a branch only once you've promoted anything worth keeping — see |
| 182 | +[Merging a branch into `main`](#merging-a-branch-into-main) below. |
| 183 | + |
| 184 | +<CodeGroup> |
| 185 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 186 | + {BranchDelete} |
| 187 | + </CodeBlock> |
| 188 | + |
| 189 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 190 | + {TsBranchDelete} |
| 191 | + </CodeBlock> |
| 192 | + |
| 193 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 194 | + {RsBranchDelete} |
| 195 | + </CodeBlock> |
| 196 | +</CodeGroup> |
| 197 | + |
| 198 | +## Merge a branch into `main` |
| 199 | + |
| 200 | +<Warning> |
| 201 | +LanceDB doesn't yet support an automatic merge of a branch into `main`, with full conflict-handling. |
| 202 | +There's no single `merge()` call that reconciles the two histories for you. |
| 203 | +</Warning> |
| 204 | + |
| 205 | +Until full merge support is available, you can promote a branch the same way any other data lands in a table: by |
| 206 | +running an ingestion workload against `main`. The only real question is how each |
| 207 | +branch row lines up with the row it should replace on `main`. This is exactly |
| 208 | +what [`merge_insert`](/tables/update#merge-incoming-rows-by-key) handles. Keyed on |
| 209 | +a unique column, it updates rows that already exist and inserts the ones that |
| 210 | +don't (an *upsert*), leaving everything else on `main` untouched. |
| 211 | + |
| 212 | +Read the rows you want out of the branch, then upsert them into `main` on your |
| 213 | +key column — `id` in this example: |
| 214 | + |
| 215 | +<CodeGroup> |
| 216 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 217 | + {BranchPromote} |
| 218 | + </CodeBlock> |
| 219 | + |
| 220 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 221 | + {TsBranchPromote} |
| 222 | + </CodeBlock> |
| 223 | + |
| 224 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 225 | + {RsBranchPromote} |
| 226 | + </CodeBlock> |
| 227 | +</CodeGroup> |
| 228 | + |
| 229 | +Since this is just a keyed write against `main`, it looks like a normal |
| 230 | +ingestion job: point the job's source at the branch and its destination at |
| 231 | +`main`. Reading the whole branch and upserting it is the simplest approach and is |
| 232 | +safe to re-run, because `merge_insert` is idempotent on the key. To promote only |
| 233 | +what changed, filter the branch read down to the rows you touched before the |
| 234 | +upsert. |
| 235 | + |
| 236 | +## Build indexes on a branch |
| 237 | + |
| 238 | +One of the most useful things a branch buys you is a safe place to build and |
| 239 | +validate an index without affecting what's in production on the `main` branch. |
| 240 | +Fork a branch, create your vector (ANN) and full-text search (FTS) |
| 241 | +indexes on it, check recall and latency, and only then promote the change to |
| 242 | +`main`. Because the indexes live on the branch, queries against `main` never see |
| 243 | +a half-built index and are never slowed down by the build. |
| 244 | + |
| 245 | +This pattern is especially valuable on <Badge color="red">Enterprise</Badge> |
| 246 | +deployments, where `main` is typically serving production traffic while you tune |
| 247 | +an index configuration on the side. |
| 248 | + |
| 249 | +<CodeGroup> |
| 250 | + <CodeBlock filename="Python" language="Python" icon="python"> |
| 251 | + {BranchIndex} |
| 252 | + </CodeBlock> |
| 253 | + |
| 254 | + <CodeBlock filename="TypeScript" language="TypeScript" icon="square-js"> |
| 255 | + {TsBranchIndex} |
| 256 | + </CodeBlock> |
| 257 | + |
| 258 | + <CodeBlock filename="Rust" language="Rust" icon="rust"> |
| 259 | + {RsBranchIndex} |
| 260 | + </CodeBlock> |
| 261 | +</CodeGroup> |
| 262 | + |
| 263 | +Schema changes such as adding, altering, or dropping columns are branch-scoped in |
| 264 | +the same way, so you can stage a larger reshaping of a table and review it before |
| 265 | +it ever reaches `main`. |
| 266 | + |
| 267 | +## Branches vs. tags vs. versions |
| 268 | + |
| 269 | +Now that you're familiar with branches, you can see how they complement the other ways LanceDB |
| 270 | +give you to work with table history. Choose these approaches based on whether you need to |
| 271 | +*label*, *read*, or *write* data at a point in history: |
| 272 | + |
| 273 | +| Feature | Writable? | Purpose | |
| 274 | +| --- | --- | --- | |
| 275 | +| **[Branch](#work-with-branches)** | ✅ Yes | Write on top of a point in history without touching `main`. | |
| 276 | +| **[Tag](/tables/versioning#tag-based-versioning)** | ❌ No | Attach a human-readable label to an existing version; protects it from cleanup. | |
| 277 | +| **[`checkout(version)`](/tables/versioning#rollback-to-previous-versions)** | ❌ No | Read a historical version of `main` without forking. Read-only until you `restore`. | |
| 278 | + |
| 279 | +<Note> |
| 280 | +For linear version history — creating versions, listing them, rolling back, and |
| 281 | +tagging — see the [Versioning and Reproducibility](/tables/versioning) guide. |
| 282 | +</Note> |
0 commit comments