What you'll achieve
By the end of this guide, you will know how to create, switch, diff, merge, and delete branches in TerminusDB using the HTTP API, TypeScript, and Python.
This guide shows you how to work with branches in TerminusDB — create isolated workspaces, make changes, review diffs, and merge back to main. Every operation includes HTTP API, TypeScript, and Python examples.
Git branch for data
Branching in TerminusDB works exactly like git branch — create a lightweight branch, make changes in isolation, then merge back when ready. Branches share history until they diverge, making them cheap to create.
Prerequisites
- TerminusDB running on
localhost:6363— see installation guide - A database with a schema. Examples use
admin/tdb-example-mydbwith aProductclass. Set up:
Example database
Connect to TerminusDB first.
# Create the database
curl -u admin:root -X POST http://localhost:6363/api/db/admin/tdb-example-mydb \
-H "Content-Type: application/json" \
-d '{"label": "My Database"}'
# Add a Product schema class
curl -u admin:root -X POST \
"http://localhost:6363/api/document/admin/tdb-example-mydb?graph_type=schema&author=setup&message=Add+schema" \
-H "Content-Type: application/json" \
-d '{"@type": "Class", "@id": "Product", "name": "xsd:string", "price": "xsd:decimal", "category": "xsd:string"}'1. Create a new branch
Create a branch from the current head of main. The new branch shares all existing history and diverges from this point forward.
HTTP API
curl -u admin:root -X POST http://localhost:6363/api/branch/admin/tdb-example-mydb/local/branch/feature \
-H "Content-Type: application/json" \
-d '{"origin": "admin/tdb-example-mydb/local/branch/main"}'Expected response:
{"@type":"api:BranchResponse","api:status":"api:success"}import TerminusClient from "@terminusdb/terminusdb-client";
const client = new TerminusClient.WOQLClient("http://localhost:6363", {
user: "admin",
key: "root",
organization: "admin",
db: "tdb-example-mydb",
});
await client.branch("feature");
// Branch "feature" created from current head of mainfrom terminusdb_client import Client
client = Client("http://localhost:6363")
client.connect(user="admin", key="root", db="tdb-example-mydb")
client.branch("feature")
# Branch "feature" created from current head of main2. List all branches
See all branches in your database.
HTTP API
curl -u admin:root "http://localhost:6363/api/db/admin/tdb-example-mydb?branches=true"Expected response:
{"path": "admin/tdb-example-mydb", "branches": ["feature", "main"]}// List branches by fetching db info
const info = await client.getDatabase("tdb-example-mydb");
console.log(info.branches);
// ["feature", "main"]# The Python client provides a branches property
branches = client.get_all_branches()
for branch in branches:
print(branch)
# {"name": "main", ...}, {"name": "feature", ...}3. Switch to a branch
There is no "checkout" command — you simply target a different branch path in your API calls. Every TerminusDB API path includes the branch:
- main:
/api/document/admin/tdb-example-mydb/local/branch/main - feature:
/api/document/admin/tdb-example-mydb/local/branch/feature
HTTP API
# Read documents from main
curl -u admin:root "http://localhost:6363/api/document/admin/tdb-example-mydb/local/branch/main?type=Product&as_list=true"
# Read documents from feature branch — just change the path
curl -u admin:root "http://localhost:6363/api/document/admin/tdb-example-mydb/local/branch/feature?type=Product&as_list=true"// Switch branch context for all subsequent operations
client.checkout("feature");
// All operations now target the feature branch
const docs = await client.getDocument({ type: "Product", as_list: true });# Switch branch context
client.checkout("feature")
# All operations now target the feature branch
docs = client.get_all_documents()4. Make changes on a branch
Insert or update documents on your branch. Changes are isolated — main is unaffected until you merge.
HTTP API
# Insert a new document on the feature branch
curl -u admin:root -X POST \
"http://localhost:6363/api/document/admin/tdb-example-mydb/local/branch/feature?author=alice&message=Add+Widget+product" \
-H "Content-Type: application/json" \
-d '{"@id": "Product/Widget", "@type": "Product", "name": "Widget", "price": 9.99, "category": "tools"}'Expected response:
["terminusdb:///data/Product/Widget"]# Update an existing document on the feature branch
curl -u admin:root -X PUT \
"http://localhost:6363/api/document/admin/tdb-example-mydb/local/branch/feature?author=alice&message=Update+Widget+price" \
-H "Content-Type: application/json" \
-d '{"@id": "Product/Widget", "@type": "Product", "name": "Widget", "price": 14.99, "category": "tools"}'Expected response:
["terminusdb:///data/Product/Widget"]client.checkout("feature");
// Insert
await client.addDocument(
{ "@type": "Product", "name": "Widget", "price": 9.99, "category": "tools" },
{ author: "alice", message: "Add Widget product" }
);
// Update
await client.updateDocument(
{ "@id": "Product/Widget", "@type": "Product", "name": "Widget", "price": 14.99, "category": "tools" },
{ author: "alice", message: "Update Widget price" }
);client.checkout("feature")
# Insert
client.insert_document(
{"@type": "Product", "name": "Widget", "price": 9.99, "category": "tools"},
commit_msg="Add Widget product"
)
# Update
client.update_document(
{"@id": "Product/Widget", "@type": "Product", "name": "Widget", "price": 14.99, "category": "tools"},
commit_msg="Update Widget price"
)5. Compare a branch to main (diff)
See exactly what changed on your branch compared to main — a structural, field-level diff.
HTTP API
curl -u admin:root -X POST http://localhost:6363/api/diff/admin/tdb-example-mydb \
-H "Content-Type: application/json" \
-d '{"before_data_version": "main", "after_data_version": "feature"}'Expected response:
[
{
"@insert": {
"@id": "Product/Widget",
"@type": "Product",
"name": "Widget",
"price": 14.99,
"category": "tools"
},
"@op": "Insert"
}
]The diff shows typed operations:
- Insert — a new document exists on the branch but not on main
- Delete — a document exists on main but not on the branch
- SwapValue — a field value changed (when the document already existed on both branches)
const diff = await client.getVersionDiff(
"admin/tdb-example-mydb/local/branch/main",
"admin/tdb-example-mydb/local/branch/feature"
);
console.log(JSON.stringify(diff, null, 2));
// Shows field-level changes between branchesdiff = client.diff_version("main", "feature")
for change in diff:
print(change)
# {"@id": "Product/Widget", "price": {"@op": "SwapValue", "@before": 9.99, "@after": 14.99}}6. Merge a branch back to main
Apply all changes from your branch onto main. If both branches modified the same field, the merge fails with a conflict report — no silent overwrites.
before_commit is the common ancestor — the commit where feature branched off main. Find it with /api/log: the most recent commit ID that appears in both branches' history. If feature was just created and main has not moved since, main's head is the common ancestor.
before_commit and after_commit accept a branch name ("main"), a commit ID, or a data-version string — "commit:<id>" or "branch:<commit-id>", the same format as the TerminusDB-Data-Version response header.
Do not pass the target branch's own tip as before_commit. A branch tip is not a common ancestor — apply then computes a plain two-way diff between the two heads, and changes the target branch made since the divergence are silently reverted or overwritten with api:status: "api:success" and no conflict raised.
HTTP API
curl -u admin:root -X POST http://localhost:6363/api/apply/admin/tdb-example-mydb/local/branch/main \
-H "Content-Type: application/json" \
-d '{
"before_commit": "commit:abc123def456",
"after_commit": "feature",
"commit_info": {
"author": "[email protected]",
"message": "Merge feature branch: add Widget product"
}
}'Expected response (success):
{"@type":"api:ApplyResponse","api:status":"api:success"}If there are conflicts (HTTP 409):
{
"@type": "api:ApplyError",
"api:status": "api:conflict",
"api:witnesses": [
{"@id": "Product/Widget", "price": {"@op": "Conflict", "@expected": 9.99, "@found": 12.99}}
]
}@expected is the base value the patch expected to find; @found is the current value on the target branch. Conflicts must be resolved manually — TerminusDB never silently picks a winner.
client.checkout("main"); // apply onto main
await client.apply(
"commit:abc123def456", // common ancestor of main and feature
"feature", // source of changes
"Merge feature branch"
);client.apply(
before_version="commit:abc123def456", # common ancestor of main and feature
after_version="feature", # source of changes
branch="main",
message="Merge feature branch: add Widget product",
author="[email protected]",
)7. Delete a branch
Remove a branch after merging. The commits remain in the database history but this branch name no longer reaches them.
HTTP API
curl -u admin:root -X DELETE http://localhost:6363/api/branch/admin/tdb-example-mydb/local/branch/featureExpected response:
{"@type":"api:BranchResponse","api:status":"api:success"}await client.deleteBranch("feature");client.delete_branch("feature")Complete workflow example
Here is the full branch workflow end-to-end — create, change, review, merge, clean up:
# 1. Create a feature branch
curl -u admin:root -X POST http://localhost:6363/api/branch/admin/tdb-example-mydb/local/branch/price-update \
-H "Content-Type: application/json" \
-d '{"origin": "admin/tdb-example-mydb/local/branch/main"}'
# 2. Make changes on the branch
curl -u admin:root -X POST \
"http://localhost:6363/api/document/admin/tdb-example-mydb/local/branch/price-update?author=alice&message=Add+new+product" \
-H "Content-Type: application/json" \
-d '{"@id": "Product/Gadget", "@type": "Product", "name": "Gadget", "price": 24.99, "category": "electronics"}'
# 3. Review what changed (diff against main)
curl -u admin:root -X POST http://localhost:6363/api/diff/admin/tdb-example-mydb \
-H "Content-Type: application/json" \
-d '{"before_data_version": "main", "after_data_version": "price-update"}'
# 4. Merge into main (before_commit = common ancestor commit from /api/log)
curl -u admin:root -X POST http://localhost:6363/api/apply/admin/tdb-example-mydb/local/branch/main \
-H "Content-Type: application/json" \
-d '{"before_commit": "commit:abc123def456", "after_commit": "price-update", "commit_info": {"author": "alice", "message": "Merge price-update into main"}}'
# 5. Clean up the branch
curl -u admin:root -X DELETE http://localhost:6363/api/branch/admin/tdb-example-mydb/local/branch/price-updateNext steps
- Version Control Operations Reference — full HTTP API reference for all version control operations
- Diff & Patch — deeper dive into structural diffing
- Time Travel — query the database at any historical commit
- WOQL Common Patterns — practical query recipes